01.java:核心包(rt.jar),sun/oracle(由sun/oracle公司提供java类库)、org(由企业或者组织提供java类库,如w3c、xml)
    a.applet
        包括 Applet 类和几个接口,用于创建 Java 小程序,处理小程序与浏览器之间的相互作用,包括声音图像等多媒体的处理;
        扩展包 javax.swing 增加了 JApplet,该类派生自 Applet,是其扩展版。applet现在主流浏览器几乎不用,可以忽略。
    b.awt
        包括许多 Java 早期的图形界面设计相关的类与接口,包括定义字体、颜色、集合绘图和图像显示等。
    c.beans
        包含与开发 beans 有关的类,即基于 JavaBeans 架构的组件,java反射就用到了此包
    d.io
        处理字节数据流(InputStream, OutputStream),字符数据流(Reader, Writer),文件操作类(File),各种输入输出功能
    e.lang
        提供利用 Java 编程语言进行程序设计的基础类,这些基础类无需使用import语句引入他们,由编译器自动引入。
        类:Object
        包装类:Boolean、Character、Double、Float、Integer、Long
        数学类:Math
        系统类:System
        运行类:Runtime
        字符串类:String, StringBuffer
        异常处理类:Throwable、Exception、Error
        线程类:Thread, Runnable
        类操作类:Class
    f.math
        用来处理任意精度的整形或浮点型数据,如BigDecimal类、BigInteger类
    g.net
        用于网络通信,实现网络功能
    h.nio
        NIO是java 1.4之后新出的一套IO接口,相对于原有标准的Java IO和Java Networking接口
        标准的IO编程接口是面向字节流和字符流的。而NIO是面向通道和缓冲区的,数据从通道中读到buffer,或从buffer写入通道;
        NIO使我们可以进行非阻塞IO操作,当数据读取到buffer中后,线程再继续处理数据。写数据也是一样的;
        NIO中的selector可以检测多个通道的事件状态(例如:链接打开,数据到达)这样单线程就可以操作多个通道的数据
    i.rmi
        提供远程方法调用接口
    j.security
        为安全框架提供类和接口
    k.sql
        用于数据库操作的一些类
    l.text
        提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口,如DateFormat、SimpleDateFormat
    m.time
        处理时间
    n.util
        提供一些常用的实用功能类,数据结构的Java实现类
        日期时间类:Date,Calendar
        随机数类:Random
        向量类:Vector
        堆栈类:Stack
        散列表类:Hashtable
        Java集合框架
        Java函数式接口:java.util.function

02.javax:x代表extension,javax是java的扩展包(rt.jar)
    a.accessibility
        定义了用户界面组件与提供对这些组件进行访问的辅助技术之间的协定
    b.activation
        activation拓展
    c.activity
        包含了解组期间通过ORB机制抛出异常的相关活动服务
    d.annotation
        annotation拓展
    e.imageio
        Java Image I/O API 的主要包
    f.jws
        使用JAX-WS(JWS)发布WebService
    g.lang.model
        用来为 Java 编程语言建立模型的包的类和层次结构
    h.management
        一个用于管理和监视的标准 API
    i.naming
        为访问命名服务提供类和接口
    j.net
        提供用于网络应用程序的类
    k.print
        为 JavaTM Print Service API 提供了主要类和接口
    l.rmi
        包含 RMI-IIOP 的用户 API
    m.script
        用来和JavaScript进行互操作,比如Java类可以调用JavaScript中的方法,而JavaScript也可调用JAVA中的方法
    k.security
        java安全机制
    l.smartcardio
        通过虚拟机建立与标准pc/sc读卡器及卡片的通信
    m.sound
        音频控制
    n.sql
        为通过 JavaTM 编程语言进行服务器端数据源访问和处理提供 API
    o.swing
        提供一组“轻量级”(全部是 Java 语言)UI组件
    p.tools
        为能够从程序(例如,编译器)中调用的工具提供接口
    q.transaction
        包含解组期间通过 ORB 机制抛出的三个异常
    r.xml
        根据 XML 规范定义核心 XML 常量和功能

1 part01

1.1 Class

01.介绍
    a.类、对象与类对象
        在Java中,类是是对具有一组相同特征或行为的实例的抽象并进行描述,对象则是此类所描述的特征或行为的具体实例;
        作为概念层次的类,其本身也具有某些共同的特性,如类名称、由类加载器去加载,都具有包,具有父类,属性和方法等;
        于是,Java中有专门定义了一个类(Class类),去描述其他类所具有的这些特性,
        因此,从此角度去看,类本身也都是属于Class类的对象。为与经常意义上的对象相区分,在此称之为”类对象”。
    b.Object类与Class类
        Object类和Class类没有直接的关系;
        Object类是一切java类的父类,对于普通的java类,即便不声明,也是默认继承了Object类;
        Class类是用于java反射机制的,一切java类,都有一个对应的Class对象。
    b.Class类
        a.图示
                                    内存状态
            类A的一个对象a1
                              ----------------->  类Class的一个对象:为类A在内存中的代理
            类A的一个对象a2

            类B的一个对象b1    ----------------->  类Class的一个对象:为类B在内存中的代理

        b.类型
            Class类的类表示正在运行的Java应用程序中的类和接口。枚举是一种类,一个注释是一种界面。
            每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。
            【原始Java类型(boolean、byte、char、short、int、long、float和double )、关键字void,也称为“类对象”】
        c.标识、对象或接口运行状态
            Java程序在运行时,Java运行时系统一直对所有对象进行所谓的运行时类型标识,即RTTI,纪录了每个对象所属的类。
            虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。
            【Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建】

02.反射
    a.定义
        关键:【反射是在运行状态中】
        【对于任意一个类,都能够知道这个类的所有属性和方法】
        【对于任意一个对象,都能够调用它的任意一个属性和方法】
        反射是Java提供的一种功能,通过反射可以无视Java的一些限制访问机制,直接使用某个类的私有变量或私有方法。
    b.步骤
        首先,通过对象得到它所对应的类型,
        然后,通过Class类提供的一些方法得到对应的变量或者方法,
        最后,再通过这些Field类和Method类直接访问某个对象的某个变量或者方法,而不是通过一般的通过.操作符进行访问
    c.功能
        在运行时,判断任意一个对象所属的类
        在运行时,构造任意一个类的对象
        在运行时,判断任意一个类所具有的成员变量和方法
        在运行时,调用任意一个对象的方法
        生成动态代理

03.反射
    a.反射入口:获取类的对象
        Class.forName("全类名")                                            --person对象
        类名.class                                                         --person对象
        对象名.getClass()                                                  --person对象
    b.构造方法:无父类构造,无法继承
        Constructor<?>[] getConstructors()                                --所有公共的构造方法
        Constructor<?>[] getDeclaredConstructors()                        --所有公共、私有的构造方法
        Constructor<T> getConstructor(类<?>... parameterTypes)            --指定公共
        Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) --指定私有
    c.属性
        Field[] getFields()                                               --所有公共的属性(父类、本类
        Field[] getDeclaredFields()                                       --所有公共、私有的属性(本类)
        Field getField(String name)                                       --指定公共
        Field getDeclaredField(String name)                               --指定私有
    d.方法
        Method[] getMethods()                                             --所有公共的方法(本类、父类、接口)
        Method[] getDeclaredMethods()                                     --所有公共、私有的方法(本类)
        Method getMethod(String name, 类<?>... parameterTypes)            --指定公共
        Method getDeclaredMethod(String name, 类<?>... parameterTypes)    --指定私有
    e.父类
        Class<? super T> getSuperclass()                                  --父类只有一个,“单继承、多实现”
    f.接口
        Class<?>[] getInterfaces()                                        --接口拥有多个,“单继承、多实现”
    g.类对象 -> 对象实例
        T newInstance()                                                   --创建由此类对象表示的类的新实例

04.反射:入口、构造方法、属性、方法、父类、接口
    a.入口
        Class.forName("全类名")           Class<Person> person = Class.forName("org.myslayers.entity.Person");
        类名.class                        Class<Person> person = Person.class;
        对象名.getClass()                 Class<Person> person = new Person().getClass();
    b.构造方法:无父类构造,无法继承
        a.代码
            public class Person {

                public Person() {

                }

                private Person(String name) {
                    this.name = name;
                }

                public Person(Integer id) {
                    this.id = id;
                }

                public Person(int id, String name, int age) {
                    this.id = id;
                    this.name = name;
                    this.age = age;
                }
            }
            public static void demo01() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                // 所有公共、私有的构造方法
                Constructor<?>[] c1s = perClazz.getDeclaredConstructors();
                for (Constructor c1 : c1s) {
                    System.out.println(c1);
                }

                // 所有公共的构造方法
                Constructor<?>[] c2s = perClazz.getConstructors();
                for (Constructor c2 : c2s) {
                    System.out.println(c2);
                }

                // 指定私有的构造方法
                Constructor<?> c3 = perClazz.getDeclaredConstructor(String.class);
                System.out.println(c3);

                // 指定公共的构造方法
                Constructor<?> c4 = perClazz.getConstructor(Integer.class);
                System.out.println(c4);
            }
        b.结果
            // 所有公共、私有的构造方法
            public reflect.Person()
            private reflect.Person(java.lang.String)
            public reflect.Person(java.lang.Integer)
            public reflect.Person(int, java.lang.String, int)

            // 所有公共的构造方法
            public reflect.Person()
            public reflect.Person(java.lang.Integer)
            public reflect.Person(int, java.lang.String, int)

            // 指定私有的构造方法
            private reflect.Person(java.lang.String)

            // 指定公共的构造方法
            public reflect.Person(java.lang.Integer)
    c.属性
        a.代码
            public class Person {
                private int id;
                private String name;
                private int age;
                public String desc;
            }
            public static void demo02() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                // 所有公共的属性(父类、本类)
                Field[] f1s = perClazz.getFields();
                for (Field f1 : f1s) {
                    System.out.println(f1);
                }

                // 所有公共、私有的属性(本类)
                Field[] f2s = perClazz.getDeclaredFields();
                for (Field f2 : f2s) {
                    System.out.println(f2);
                }
            }
        b.结果
            // 所有公共的属性(父类、本类)
            public java.lang.String reflect.Person.desc

            // 所有公共、私有的属性(本类)
            private int reflect.Person.id
            private java.lang.String reflect.Person.name
            private int reflect.Person.age
            public java.lang.String reflect.Person.desc
    d.方法
        a.代码
            public class Person implements MyInterface {
                @Override
                public void interfaceMethod() {
                    System.out.println("interface Method...");
                }

                public int getId() {
                    return id;
                }

                public void setId(int id) {
                    this.id = id;
                }
            }
            public static void demo03() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                // 所有公共的方法(本类、父类、接口)
                Method[] m1s = perClazz.getMethods();
                for (Method m1 : m1s) {
                    System.out.println(m1);
                }

                // 所有公共、私有的方法(本类)
                Method[] m2s = perClazz.getDeclaredMethods();
                for (Method m2 : m2s) {
                    System.out.println(m2);
                }
            }
        b.结果
            // 所有公共的方法(本类、父类、接口)
            public void reflect.Person.interfaceMethod()                                              --接口
            public int reflect.Person.getId()                                                         --本类
            public void reflect.Person.setId(int)                                                     --本类
            public final void java.lang.Object.wait() throws java.lang.InterruptedException           --父类
            public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException   --父类
            public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException--父类
            public boolean java.lang.Object.equals(java.lang.Object)                                  --父类
            public java.lang.String java.lang.Object.toString()                                       --父类
            public native int java.lang.Object.hashCode()                                             --父类
            public final native java.lang.Class java.lang.Object.getClass()                           --父类
            public final native void java.lang.Object.notify()                                        --父类
            public final native void java.lang.Object.notifyAll()                                     --父类

            // 所有公共、私有的方法(本类)
            public int reflect.Person.getId()                                                         --本类
            public void reflect.Person.setId(int)                                                     --本类
    e.父类
        a.代码
            public class Person {

            }
            public static void demo05() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                // 父类只有一个,“单继承、多实现”
                Class<?> superclass = perClazz.getSuperclass();
                System.out.println(superclass);
            }
        b.结果
            Class java.lang.Object
    f.接口
        a.代码
            public class Person implements MyInterface, MyInterface2 {
                @Override
                public void interfaceMethod() {
                    System.out.println("interface Method...");
                }

                @Override
                public void interface2Method() {
                    System.out.println("interface2 Method...");
                }
            }
            public static void demo05() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                // 接口拥有多个,“单继承、多实现”
                Class<?>[] interfaces = perClazz.getInterfaces();
                for (Class<?> inter:interfaces){
                    System.out.println(inter);
                }
            }
        b.结果
            interface reflect.MyInterface
            interface reflect.MyInterface2

05.反射:类对象 -> 对象实例、类对象构造方法 -> 对象实例
    a.类对象 -> 创建实例
        a.代码
            public static void demo01() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                Object instance = perClazz.newInstance();
                Person per = (Person) instance;
                System.out.println(per);
            }
        b.结果
            reflect Person@1b6d3586
    b.类对象 -> 创建实例 -> 强转为Person类型,间接访问属性(setter、getter)
        a.代码
            public static void demo02() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                Person per = (Person) perClazz.newInstance();
                per.setName("zs");
                System.out.println(per.getName());
            }
        b.结果
            zs
    c.类对象 -> 创建实例 -> 强转为Person类型,直接访问属性(xxxFiled.set(per, 1))
        a.代码
            public static void demo03() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                Person per = (Person) perClazz.newInstance();
                Field idFiled = perClazz.getDeclaredField("id");
                idFiled.setAccessible(true);            // private修饰的id,通过setAccessible(true)修改访问权限
                idFiled.set(per,1);                     // per.setId(1);
                System.out.println(per.getId());
            }
        b.结果
            1
    d.类对象 -> 创建实例 -> 操作方法:普通方法(public)、无参方法(private,修改权限)、有参方法(private,修改权限)
        a.代码
            public static void demo04() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                Person per = (Person) perClazz.newInstance();

                // 普通方法(public)
                per.interfaceMethod();

                // 无参方法(private,修改权限)
                Method method = perClazz.getDeclaredMethod("privateMethod", null);
                method.setAccessible(true);
                method.invoke(per, null);

                // 有参方法(private,修改权限)
                Method method2 = perClazz.getDeclaredMethod("privateMethod2", String.class);
                method2.setAccessible(true);
                method2.invoke(per, "zs");
            }
        b.结果
            // 普通方法(public)
            interface method...

            // 无参方法(private,修改权限)
            private method...

            // 有参方法(private,修改权限)
            private method...zs
    e.类对象构造方法 -> 对象实例
        a.代码
            public static void demo05() throws Exception {
                Class<?> perClazz = Class.forName("reflect.Person");

                // 根据public无参构造方法获取实例
                Constructor<?> c1 = perClazz.getConstructor();
                Person p1 = (Person) c1.newInstance();
                System.out.println(p1);

                // 根据public有参构造方法获取实例
                Constructor<?> c2 = perClazz.getConstructor(Integer.class);
                Person p2 = (Person) c2.newInstance(12);
                System.out.println(p2);

                // 根据private有参构造方法获取实例(private,修改权限)
                Constructor<?> c3 = perClazz.getDeclaredConstructor(String.class);
                c3.setAccessible(true);
                Person p3 = (Person) c3.newInstance("zs");
                System.out.println(p3);
            }
        b.结果
            // 根据public无参构造方法获取实例
            reflect.Person@1b6d3586

            // 根据public有参构造方法获取实例
            reflect.Person@1b6d3586

            // 根据private有参构造方法获取实例(private,修改权限)
            reflect.Person@1b6d3586

06.反射:动态加载类名和方法、反射可以越过泛型检查、自定义set方法
    a.动态加载类名和方法
        a.代码
            public static void demo01() throws Exception {
                // 加载txt文件
                Properties prop = new Properties();
                prop.load(new FileReader("class.txt"));

                // 加载类名
                String classname = prop.getProperty("classname");

                // 加载方法名
                String methodname = prop.getProperty("methodname");

                Class<?> perClazz = Class.forName(classname);
                Method method = perClazz.getMethod(methodname);
                method.invoke(perClazz.newInstance());
            }
        b.结果
            static Method ...
    b.反射可以越过泛型检查(虽然反射可以访问private等访问修饰符不允许访问的属性/方法,但忽略了泛型约束,易程序混乱)
        a.代码
            public static void demo02() throws Exception {
                ArrayList<Integer> list = new ArrayList<>();
                list.add(123);
                list.add(3);
                list.add(2);

                Class<?> listClazz = list.getClass();
                Method method = listClazz.getMethod("add", Object.class);
                method.invoke(list, "zs...");                      // 使用正确,对象名为原数组list
                System.out.println(list);
            }
        b.结果
            [123, 3, 2, zs...]
        c.代码
            public static void demo02() throws Exception {
                ArrayList<Integer> list = new ArrayList<>();
                list.add(123);
                list.add(3);
                list.add(2);

                Class<?> listClazz = list.getClass();
                Method method = listClazz.getMethod("add", Object.class);
                method.invoke(listClazz.newInstance(), "zs...");   // 错误,listClazz.newInstance()与list两对象
                System.out.println(list);
            }
        d.结果
            [123, 3, 2]
    c.自定义set方法
        a.自定义PropertyUtil工具类
            package reflect;
            import java.lang.reflect.Field;
            public class PropertyUtil {
                public static void setProperty(Object obj,String propertyName,Object value) throws Exception{
                    Class<?> clazz = obj.getClass();
                    Field field = clazz.getDeclaredField(propertyName);
                    field.setAccessible(true);
                    field.set(obj, value);
                }
            }
        b.代码
            public static void demo02() throws Exception {
                // 给Person赋值
                Person per = new Person();
                PropertyUtil.setProperty(per, "name", "zs");
                PropertyUtil.setProperty(per, "age", 23);

                // 给Student赋值
                Student stu = new Student();
                PropertyUtil.setProperty(stu, "score", 98);

                //打印
                System.out.println(per.getName() + "," + per.getAge());
                System.out.println(stu.getScore());
            }
        c.结果
            zs,23
            98

1.2 Object

01.介绍
    a.编辑器:【JDK 6之前,当没有父类时,由编译器在编译时自动为其指定一个父类】
        在编译源代码时,当一个类没有显式标明继承的父类时,编译器会为其指定一个默认的父类(一般为Object),
        而交给虚拟机处理这个类时,由于这个类已经有一个默认的父类了,因此,JVM仍然会按照常规的方法来处理这个类。
        对于这种情况,从编译后的二进制角度来看,所有的类都会有一个父类(后面可以以此依据来验证)。
    b.虚拟机:【JDK 7之后,当遇到没有父类的类时,JVM自动将这个类看成是Object类的子类】
        编译器按照实际代码进行编译,并不会做额外的处理,即如果一个类没有显式地继承于其他类时,编译后代码仍没有父类;
        然后由虚拟机运行二进制代码时,当遇到没有父类的类时,就会自动将这个类看成是Object类的子类
    c.Object类(1+4+3+5=13)
        无声明的构造方法                                对于未定义构造函数的类,默认【存在一个无参数的构造方法】
        --------------------------------------------------------------------------------------------------
        private static native void registerNatives();                      注册,类加载时,来注册绑定本地方法
        public final native Class <?> getClass()                           反射,返回一个对象运行时的实例类
        protected native Object clone() throws CloneNotSupportedException; 克隆,创建与该对象的类相同的新对象
        protected void finalize() throws Throwable { }                     回收,堆空间对象没有被栈空间变量引用
        --------------------------------------------------------------------------------------------------
        public native int hashCode();                                      哈希,相等的对象必须具有相等的哈希码
        public boolean equals(Object obj) { return (this == obj); }        比较,比较两对象是否相等
        public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
        --------------------------------------------------------------------------------------------------
        public final void wait() throws InterruptedException { wait(0); }  导致当前线程等待,notify唤醒/时间已过
        public final native void wait(long timeout) throws InterruptedException;
        public final void wait(long timeout, int nanos) throws InterruptedException { ... }
        public final native void notify();                                 随机唤醒【单个线程】,程序无法控制
        public final native void notifyAll();                              唤醒【所有线程】,各线程再争夺对象的锁

02.Object类
    a.无,构造方法
        a.方法
            无
        b.解释
            JAVA规定,对于未定义构造函数的类,默认【存在一个无参数的构造方法】,Object类充分体现,【反编译javap验证】
    b.注册
        a.方法
            private static native void registerNatives();
        b.解释
            【在类加载的时候会执行该方法,通过该方法来注册绑定本地方法】
            private:修饰该方法无法被调用
            native:将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦
    c.反射
        a.方法
            public final native Class <?> getClass()    // 等价于Object.class
        b.解释
            返回【运行时类对象Class <?>,被表示类的static synchronized方法锁定的对象】
            【特别地,在求一个匿名内部类的 Class 类型,并没有一个对应的有名字的类,其类名只是系统自动生成的一个编号类】
            实际结果的类型是Class<? extends |X|>,其中|X|是静态类型上其表达的擦除getClass被调用。
            例如,在此代码片段中不需要转换:
            Number n = 0;
            Class<? extends Number> c = n.getClass();
        c.使用
            【获取两个对象的类型从而判断某两个对象是否是同一个类】,或【反射】
    d.克隆
        a.方法
            protected native Object clone() throws CloneNotSupportedException;
        b.解释
            clone函数返回的是一个引用(“浅拷贝”),指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。
        c.调用
            这是一个protected方法,作用在本类、子类,同包类中;若要在某个类中使用该方法,需要重写重写clone()方法:
            某个类如果要使用clone()方法进行自身复制,就必须实现Cloneable接口,否则就会抛出CloneNotSupportedException
            但是这个接口本身并没有任何方法,真正的要实现复制还是要靠重写父类的clone()方法,接口只是起了标识作用;
            public class A implements Cloneable{
                @Override
                protected Object clone() throws CloneNotSupportedException {
                    return super.clone();
                }
            }
            -------------------------------------------------------------------------------------------------
            所有数组都被认为是实现接口Cloneable,并且数组类型T[]的clone方法的返回类型是T[],T是任何引用或原始类型;
            否则该方法将创建该对象的类的新实例,并将其所有字段初始化为完全符合该对象的相应字段的内容,就像通过赋值一样,
            这些字段的内容本身不被克隆。因此,该方法执行该对象的“浅拷贝”,而不是“深度拷贝”操作。
        d.浅拷贝
            如果【成员变量是引用数据类型】,复制后的对象与原始的对象【共用同一个成员变量】:
            对【复制后的对象中成员变量的更改】也会出现在【原始的对象】中,因为它们共同引用的是堆中的同一个实例
            -------------------------------------------------------------------------------------------------
            public class A implements Cloneable{
                private int id;                     // 原生数据类型,默认深拷贝,不受影响
                private String username;            // 引用数据类型,默认浅拷贝,深拷贝【手动增加对成员变量的复制】
                private String password;            // 引用数据类型,默认浅拷贝,深拷贝【手动增加对成员变量的复制】
                private B b = new B();              // 引用数据类型,默认浅拷贝,深拷贝【手动增加对成员变量的复制】

                @Override
                protected Object clone() throws CloneNotSupportedException {
                    return super.clone();
                }
            }
            public class B implements Cloneable{
                int a = 1;

                @Override
                protected Object clone() throws CloneNotSupportedException {
                    return super.clone();
                }
            }
        e.深拷贝
            如果【成员变量是引用数据类型】,复制后的对象与原始的对象【不共用同一个成员变量】:
            【复制这个对象的所有成员变量的实例,而不是复制引用】,默认clone()无法实现,需要【手动增加对成员变量的复制】
            -------------------------------------------------------------------------------------------------
            public class A implements Cloneable{
                private int id;                 // 原生数据类型,默认深拷贝,不受影响
                private String username;        // 引用数据类型,默认浅拷贝,深拷贝【手动增加对成员变量的复制】
                private String password;        // 引用数据类型,默认浅拷贝,深拷贝【手动增加对成员变量的复制】
                private B b = new B();          // 引用数据类型,默认浅拷贝,深拷贝【手动增加对成员变量的复制】

                @Override
                protected Object clone() throws CloneNotSupportedException {
                    A a = (A) super.clone();
                    a.b = (B) b.clone();
                    return a;
                }
            }
            public class B implements Cloneable{
                int a = 1;

                @Override
                protected Object clone() throws CloneNotSupportedException {
                    return super.clone();
                }
            }
    e.回收器
        a.方法
            protected void finalize() throws Throwable { }
        b.解释
            当【一个堆空间中的对象没有被栈空间变量引用】时,垃圾收集器在对象上调用该finalize()方法。
            -------------------------------------------------------------------------------------------------
            finalize规定,如果Java虚拟机已经确定不再有任何方法可以被任何尚未死亡的线程访问的方法被调用,
            除非是由于最后确定的其他对象或类的准备工作所采取的行动。finalize方法可以采取任何行动,
            包括使此对象再次可用于其他线程;然而,finalize的通常目的是在对象不可撤销地丢弃之前执行清除动作。
            例如,表示输入/输出连接的对象的finalize方法可能会在对象被永久丢弃之前执行显式I/O事务来中断连接。
            -------------------------------------------------------------------------------------------------
            Java不能保证哪个线程将为任何给定对象调用finalize方法,但是确保调用finalize的线程在调用finalize时,
            不会持有任何用户可见的同步锁。如果finalize方法抛出未捕获的异常,则会忽略该异常,并终止该对象的定类。
            -------------------------------------------------------------------------------------------------
            在为对象调用finalize方法之后,在Java虚拟机再次确定不再有任何方式可以通过任何尚未被死亡的线程访问
            此对象的任何方法的情况下,将采取进一步的操作,包括可能的操作由准备完成的其他对象或类别,此时可以丢弃对象。
            -------------------------------------------------------------------------------------------------
            finalize方法从不被任何给定对象的Java虚拟机调用多次
            finalize方法抛出的任何异常都会导致该对象的终止被停止,否则被忽略
        c.使用
            理论上来说,finalize()方法会在垃圾回收机制回收当前对象之前调用,开发者可以重写某个类的这个方法,
            完成一些类似于资源释放的问题。但是事实却并不如此,由于某些原因,这些已经被认为需要收集的对象,
            可能会重新成为活跃的对象,从而打断回收过程。
            -------------------------------------------------------------------------------------------------
            并且,Java 并不保证finalize()方法一定会在对象被回收之前调用,但能够保证这个方法不会持有锁,
            当因为某些原因这个方法抛出异常的时候,Java 会忽略异常并终止这个方法的执行。
            -------------------------------------------------------------------------------------------------
            所以从Java 9开始,将finalize()方法标识成了Deprecated,不建议开发者继续使用这个方法完成某些功能。
            -------------------------------------------------------------------------------------------------
            同时 Java 还为释放资源等需要推荐了别的方式,如使用弱引用引用某个对象,能够使被引用的对象不会因为弱引用的
            存在而不能被回收,配合AutoCloseable接口,Cleaner类实现更加灵活、更加高效的资源释放的功能。
        d.GC特点
            垃圾对象:一个堆空间中的对象没有被栈空间变量引用
            垃圾回收:GC主要针对堆内存,GC销毁垃圾对象,释放空间
            自动回收机制:JVM内存耗尽,一次性回收全部垃圾对象
            手动回收机制:无法直接命令GC【马上回收】,但可以【尽快回收】(System.gc()、Runtime.getRuntime().gc())

03.Object类
    a.wait
        a.方法
            public final void wait() throws InterruptedException { wait(0); }
            导致当前线程等待,直到另一个线程调用该对象的notify()或notifyAll()
            -----------------------------------------------------------------------------------------------
            public final native void wait(long timeout) throws InterruptedException;
            导致当前线程等待,直到另一个线程调用该对象的notify()或notifyAll(),或者指定的时间已过
            -----------------------------------------------------------------------------------------------
            public final void wait(long timeout, int nanos) throws InterruptedException { ... }
            导致当前线程等待,直到另一个线程调用该对象的notify()或notifyAll(),或者指定的时间已过,或一定量的实时时间
        b.参数
            timeout - 以毫秒为单位等待的最长时间
            nanos - 额外的时间,以纳秒范围0-999999
        c.解读
            三种重载,最终都会调用wait(long)方法,
            【当前线程进入堵塞状态,并释放对象的锁】,【直到被唤醒或超时或打断后,且重新获取到锁后,才能继续执行】
            -----------------------------------------------------------------------------------------------
            调用某个对象的wait()方法的时候,当前线程需要有这个对象的锁,否则抛出IllegalMonitorStateException;
            一般情况下是使用synchronized可以获取锁,【调用了wait()方法之后当前线程就会进入堵塞状态并释放对象的锁】;
            -----------------------------------------------------------------------------------------------
            这时其他需要当前对象锁的线程就可以进入,并且这个线程可以通过调用notify()方法,
            使这个被堵塞的线程进入等待锁状态,直到当前正在运行的线程释放了锁之后,
            这个线程就可以再获取对象锁然后接着执行wait()方法后面的语句
    b.notify
        a.方法
            public final native void notify();                          随机唤醒【单个线程】,程序无法控制
            -----------------------------------------------------------------------------------------------
            public final native void notifyAll();                       全部唤醒【所有线程】,各线程再争夺对象的锁
        b.解读
            方法调用后,其所在线程不会立即释放所持有的锁,直到其所在同步代码块中的代码执行完毕,此时释放锁,
            因此,如果其同步代码块后还有代码,其执行则依赖于JVM的线程调度

04.Object类
    a.toString
        a.方法
            public String toString() {
                return getClass().getName() + "@" + Integer.toHexString(hashCode());
            }
        b.解释
            返回对象的字符串表示形式,默认格式如下:【类的名称 + @ + 对象的哈希码的无符号的十六进制】
            一般来说,结果应该是一个简明扼要的表达,容易让人阅读,【建议所有子类覆盖此方法】,如List、Map等数据结构
    b.hashCode
        a.方法
            public native int hashCode();
        b.解释
            返回对象的哈希码值,native方法通过内存所在的内存块来计算得出,Java本身无法直接操作内存
        c.用于一些哈希表的实现,如Java中的HashMap
            JVM运行期间,如果equals比较中所用信息没有被修改,同一个对象多次调用hashCode()方法时的结果必须相同
            如果两个对象调用equals(Object)返回的结果【相等】,那么它们的hashCode【必须相同】
            如果两个对象调用equals(Object)返回的结果【不等】,那么它们的hashCode【可能相同】【也可能不同】
            -------------------------------------------------------------------------------------------------
            对象相等 -> hashcode一定相同            √        hash相同的对象 -> 对象可能相同、也可能不同 √
            对象不等 -> hashcode可能相同,也可能不同 √        hash不同的对象 -> 对象一定不同            √
            -------------------------------------------------------------------------------------------------
            【无论何时覆盖equals方法,通常覆盖hashCode方法,以便维护"相等的对象必须具有相等的哈希码"这一规定】
        d.图示
            放入集合中的对象(HashTable、HashMap、HashSet等哈希结构)
                                     ↓
                判断【是否集合中任意一个对象的hashCode值相等?】——————————————→
                                     ↓                                     ↓
                                     ↓  是                                 ↓ 否
                                     ↓                                     ↓
                判断【是否与集合中任意一个对象equals比较相等?】——————————→【放入对象】
                                     ↓
                                     ↓  是
                                     ↓
                                  【舍弃】
        e.阿里规约
            只要重写 equals,就必须重写 hashCode
            如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals。
            因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。
            【String 重写了 hashCode 和 equals 方法,所以可以使用 String 对象作为 key 来使用。】
    c.equals
        a.方法
            // 同一对象就是指内存中同一块存储单元
            // 如果this和obj指向的是同一块内存对象,则返回true,否则返回false(内容相同、内存不同,则返回false)
            public boolean equals(Object obj) {
                return (this == obj);
            }
        b.五条性质:同一个类,满足上述5条规则;子类与父类混用,默认IDEA生成的equals,不符合【对称性】【传递性】
            自反性:如果有x.equals(x),那么必须返回true
            对称性:如果有x.equals(y),那么必须有y.equals(x)
            传递性:如果有x.equals(y)=ture、y.equals(z)=true,那么必须有x.equals(z) = true
            恒定性:对于两个确定的对象x、y,在任何时候x.equals(y)的结果一定是相同的
            对于任何一个不为空的对象x,x.equals(null)必须返回 false
        c.必要性:重写equals(),必须重写hashCode()
            对于任何非空的参考值x和y,当且仅当x和y引用相同的对象(x==y具有值true)时,该方法返回true。
            【无论何时覆盖equals方法,通常覆盖hashCode方法,以便维护"相等的对象必须具有相等的哈希码"这一规定】
        d.集合结构:必须重写equals()
            除了我们判断两个对象是否相等使用equals()方法之外,一些数据结构也需要使用这个方法,如HashMap、ArrayList
            在哈希表中虽然要先根据key的hashCode决定索引位置,但是之后查找还是要【根据equals()方法判断key是否相等】
        f.判等问题
            ==:关系运算符,对象内存地址,【原生数据类型(地址中储存数值),引用数据类型(地址中存储指向堆中的地址)】
            equals:默认Object的equals()方法,若父类及子类重写了Object的equals,则判断根据重写规则
            -------------------------------------------------------------------------------------------------
            原生数据类型:【直接存于栈中的,没有"没有引用和实例的区分"】
            引用数据类型:【"引用"存放于栈中】【"实例"存放于堆中】
            -------------------------------------------------------------------------------------------------
            原生数据类型:【基本数据类型 == 比较数值】【基本数据类型 equals 比较数值,调用Object的equals】
            引用数据类型:【引用数据类型 == 比较地址】【引用数据类型 equals 先地址,再数值,调用String的equals】
            -------------------------------------------------------------------------------------------------
            一个未继承父类、未实现接口的类,重写equals()保证【先比较地址,再比较类型,最后比较内容】,再重写hashCode
            public class Student {
                public String name;
                public int age;

                @Override
                public boolean equals(Object o) {                   // IDEA 自动生成的equals方法
                    if (this == o) return true;
                    if (!(o instanceof Student)) return false;
                    Student student = (Student) o;
                    return Objects.equals(this.name, student.getName) && this.age == student.age;
                }

                @Override
                public int hashCode() {
                    return Objects.hash(getName(), getAge()         // IDEA 自动生成的hashCode方法
                }
            }

05.面试题一
    a.equals()的【五条性质】
        a.同一个类,满足上述5条规则
            public class Car {
                private int batch;

                public Car(int batch) {
                    this.batch = batch;
                }

                @Override
                public boolean equals(Object o) {
                    if (this == o) return true;
                    if (!(o instanceof Car)) return false;          // bc.equals(c) 需比较 c instanceof BigCar
                    Car car = (Car) o;
                    return batch == car.batch;
                }

                @Override
                public int hashCode() {
                    return Objects.hash(batch);
                }

                public static void main(String[] args) {
                    Car c1 = new Car(1);
                    Car c2 = new Car(1);
                    Car c3 = new Car(1);
                    System.out.println("1.自反性:x.equals(x),那么必须返回true");
                    System.out.println(c1.equals(c1));

                    System.out.println("2.对称性:x.equals(y),那么必须有y.equals(x)");
                    System.out.println(c1.equals(c2));
                    System.out.println(c2.equals(c1));

                    System.out.println("3.传递性:x.equals(y)=ture、y.equals(z)=true,必须x.equals(z) = true");
                    System.out.println(c1.equals(c2));
                    System.out.println(c2.equals(c3));
                    System.out.println(c1.equals(c3));

                    System.out.println("4.一致性:对于两个确定的对象x、y,在任何时候x.equals(y)的结果一定相同");
                    for (int i = 0; i < 50; i++) {
                        if (c1.equals(c2) != c1.equals(c2)) {
                            System.out.println("equals方法没有遵守一致性!");
                            break;
                        }
                    }
                    System.out.println("equals方法遵守一致性!");

                    System.out.println("5.对于任何一个不为空的对象x,x.equals(null)必须返回false:");
                    System.out.println(c1.equals(null));
                }
            }
            -------------------------------------------------------------------------------------------------
            1.自反性:x.equals(x),那么必须返回true:
            true
            2.对称性:x.equals(y),那么必须有y.equals(x):
            true
            true
            3.传递性:x.equals(y)=ture、y.equals(z)=true,必须x.equals(z) = true
            true
            true
            true
            4.一致性:对于两个确定的对象x、y,在任何时候x.equals(y)的结果一定相同
            equals方法遵守一致性!
            5.对于任何一个不为空的对象x,x.equals(null)必须返回false:
            false
        b.子类与父类混用,虽然默认IDEA生成的equals,不符合【对称性】【传递性】,但子类可以重写equals改变规则
            public class Car {
                private int batch;

                public Car(int batch) {
                    this.batch = batch;
                }

                @Override
                public boolean equals(Object o) {
                    if (this == o) return true;
                    if (!(o instanceof Car)) return false;
                    Car car = (Car) o;
                    return batch == car.batch;
                }

                @Override
                public int hashCode() {
                    return Objects.hash(batch);
                }
            }
            public class BigCar extends Car{
                int count;

                public BigCar(int batch, int count) {
                    super(batch);
                    this.count = count;
                }

                @Override
                public boolean equals(Object o) {
                    if (this == o) return true;
                    if (!(o instanceof BigCar)) return false;
                    if (!super.equals(o)) return false;
                    BigCar bigCar = (BigCar) o;
                    return count == bigCar.count;
                }

                @Override
                public int hashCode() {
                    return Objects.hash(super.hashCode(), count);
                }

                public static void main(String[] args) {
                    Car c = new Car(1);
                    BigCar bc = new BigCar(1, 20);
                    System.out.println(c.equals(bc));       // BigCar肯定属于Car类型
                    System.out.println(bc.equals(c));       // Car不一定是BigCar类型,源码c instanceof BigCar

                    BigCar bc2 = new BigCar(1, 20);
                    System.out.println(bc.equals(bc2));
                    System.out.println(bc2.equals(bc));
                }
            }
            -------------------------------------------------------------------------------------------------
            true   子类与父类混用,虽然默认IDEA生成的equals,不符合【对称性】【传递性】,但子类可以重写equals改变规则
            false  子类与父类混用,虽然默认IDEA生成的equals,不符合【对称性】【传递性】,但子类可以重写equals改变规则
            true   同一个类,满足上述5条规则
            true   同一个类,满足上述5条规则
    b.equals()的【必要性:重写equals(),必须重写hashCode()】
        a.代码
            public class Demo {
                public static void main(String[] args) {
                    String s = "ok";
                    StringBuilder sb = new StringBuilder(s);

                    System.out.println(s.hashCode() + "  " + sb.hashCode());    // 3548  1829164700

                    String t = new String("ok");
                    StringBuilder tb = new StringBuilder(s);
                    System.out.println(t.hashCode() + "  " + tb.hashCode());    // 3548  2018699554
                }
            }
        b.分析
            String          "s"、"t"     hashcode值相同     String类重写了hashCode方法
            StringBuilder   "sb"、"tb"   hashcode值不同     StringBuilder类未重写hashCode方法、未重写equals方法
            -------------------------------------------------------------------------------------------------
            对于任何非空的参考值x和y,当且仅当x和y引用相同的对象(x==y具有值true)时,该方法返回true。
            【无论何时覆盖equals方法,通常覆盖hashCode方法,以便维护"相等的对象必须具有相等的哈希码"这一规定】
            -------------------------------------------------------------------------------------------------
            推荐:使用Objects.hash(),或者Arrays.hashCode(),来生成hashCode值
    c.equals()的【集合结构:必须重写equals()】
        a.HashList
            public boolean equals(Object o) {
                if (o == this)
                    return true;
                if (!(o instanceof List))
                    return false;

                ListIterator<E> e1 = listIterator();
                ListIterator<?> e2 = ((List<?>) o).listIterator();
                while (e1.hasNext() && e2.hasNext()) {
                    E o1 = e1.next();
                    Object o2 = e2.next();
                    if (!(o1==null ? o2==null : o1.equals(o2)))
                        return false;
                }
                return !(e1.hasNext() || e2.hasNext());
            }
        b.分析
            除了我们判断两个对象是否相等使用equals()方法之外,一些数据结构也需要使用这个方法,如HashMap、ArrayList
            在哈希表中虽然要先根据key的hashCode决定索引位置,但是之后查找还是要【根据equals()方法判断key是否相等】
    d.ArrayList的contains(Object o):如果此列表包含指定的元素,则返回true
        a.代码
            public class Demo {
                static class A {
                    @Override
                    public boolean equals(Object obj) {
                        return obj instanceof A;
                    }
                }

                static class B extends A{
                    @Override
                    public boolean equals(Object obj) {
                        return obj instanceof B;
                    }
                }

                public static void main(String[] args) {
                    List<A> list = new ArrayList<>();
                    A a = new A();
                    B b = new B();  // B 继承 A,并重写 equals()

                    // contains(Object o):如果此列表包含指定的元素,则返回true,
                    // 返回true当且仅当此列表包含至少一个元素e,(o==null ? e==null : o.equals(e))

                    list.add(a);
                    System.out.println(list.contains(a));   // true
                    System.out.println(list.contains(b));   // false

                    list.clear();

                    list.add(b);
                    System.out.println(list.contains(a));   // true     B 继承 A,并重写 equals()
                    System.out.println(list.contains(b));   // true     B 继承 A,并重写 equals()
                }
            }
        b.分析
            public boolean contains(Object o) {
                return indexOf(o) >= 0;
            }

            public int indexOf(Object o) {
                if (o == null) {
                    for (int i = 0; i < size; i++)
                        if (elementData[i]==null)
                            return i;
                } else {
                    for (int i = 0; i < size; i++)
                        if (o.equals(elementData[i]))
                            return i;
                }
                return -1;
            }
            ------------------------------------------------------------------------------------------------
            list调用contains(Object o):如果此列表包含指定的元素,则返回true
            contains调用indexOf,判断【o.equals(elementData[i])】,即,【elementData[i] instanceof o】
            ------------------------------------------------------------------------------------------------
            list.add(a);
            System.out.println(list.contains(a));   // true    a instanceof A
            System.out.println(list.contains(b));   // false   a instanceof B   显然错误

            list.clear();

            list.add(b);
            System.out.println(list.contains(a));   // true    b instanceof A   B 继承 A,并重写 equals()
            System.out.println(list.contains(b));   // true    b instanceof B
    e.重写equals()中getClass与instanceof的区别
        a.代码
            public class Person {
                    protected String name;
                    public String getName() {
                        return name;
                    }
                    public void setName(String name) {
                        this.name = name;
                    }
                    public Person(String name){
                        this.name = name;
                    }
                    public boolean equals(Object object){
                        if(object instanceof Person){
                            Person p = (Person) object;
                            if(p.getName() == null || name == null){
                                return false;
                            }
                            else{
                                return name.equalsIgnoreCase(p.getName ());
                            }
                        }
                        return false;
                   }
                }
            }
            -----------------------------------------------------------------------------------------------
            public class Employee extends Person{
                    private int id;
                    public int getId() {
                        return id;
                    }
                    public void setId(int id) {
                        this.id = id;
                    }
                    public Employee(String name,int id){
                        super(name);
                        this.id = id;
                    }
                    /**
                     * 重写equals()方法
                     */
                    public boolean equals(Object object){
                        if(object instanceof Employee){
                            Employee e = (Employee) object;
                            return super.equals(object) && e.getId() == id;
                        }
                        return false;
                    }
                }
            }
            -----------------------------------------------------------------------------------------------
            public class Demo {
                public static void main(String[] args) {
                    Employee e1 = new Employee("chenssy", 23);
                    Employee e2 = new Employee("chenssy", 24);
                    Person p1 = new Person("chenssy");
                    System.out.println(p1.equals(e1));      // true
                    System.out.println(p1.equals(e2));      // true
                    System.out.println(e1.equals(p1));      // false
                }
            }
        b.分析
            如果所有子类都拥有统一的语义,用instanceof;否则,覆写equals时推荐使用getClass进行类型判断
            p1.equals(e1)   // true     Employee instanceof Person
            p1.equals(e2)   // true     Employee instanceof Person
            e1.equals(p1)   // false    Person instanceof Employee  显然错误
    f.编写一个完美的equals建议,出自【Java核心技术 第一卷:基础知识】
        a.建议1
            显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量(参数名命名,强制转换请参考建议5)
        b.建议2
            检测this与otherObject是否引用同一个对象:if(this == otherObject) return true;
        c.建议3
            检测otherObject是否为null,如果为null,返回false:if(otherObject == null) return false;
        d.建议4
            比较this与otherObject是否属于同一个类:
                如果equals在每个子类中有所改变,用getClass:if(getClass()!=otherObject.getClass()) return false;
                如果所有子类都拥有统一的语义,用instanceof:if(!(otherObject instanceof ClassName)) return false;
        e.建议5
            将otherObject转换为相应的类类型变量:ClassName other = (ClassName) otherObject;
        f.建议6
            使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true,否则就返回flase:
                如果在子类中重新定义equals,就要在其中包含调用super.equals(other)
                当此方法被重写时,通常有必要重写hashCode方法,维护hashCode方法的规定(相等对象必须具有相等的哈希码)

06.面试题二
    a.问题1
        a.提问
            == 和 equals 的区别?
        b.回答
            ==:关系运算符,对象内存地址,【原生数据类型(地址中储存数值),引用数据类型(地址中存储指向堆中的地址)】
            equals:默认Object的equals()方法,若父类及子类重写了Object的equals,则判断根据重写规则
            -------------------------------------------------------------------------------------------------
            原生数据类型:【直接存于栈中的,没有"没有引用和实例的区分"】
            引用数据类型:【"引用"存放于栈中】【"实例"存放于堆中】
            -------------------------------------------------------------------------------------------------
            原生数据类型:【基本数据类型 == 比较数值】【基本数据类型 equals 比较数值,调用Object的equals】
            引用数据类型:【引用数据类型 == 比较地址】【引用数据类型 equals 先地址,再数值,调用String的equals】
            -------------------------------------------------------------------------------------------------
            一个未继承父类、未实现接口的类,重写equals()保证【先比较地址,再比较类型,最后比较内容】,再重写hashCode
            public class Student {
                public String name;
                public int age;

                @Override
                public boolean equals(Object o) {                   // IDEA 自动生成的equals方法
                    if (this == o) return true;
                    if (!(o instanceof Student)) return false;      // bc.equals(c) 需比较 c instanceof BigCar
                    Student student = (Student) o;
                    return Objects.equals(this.name, student.getName) && this.age == student.age;
                }

                @Override
                public int hashCode() {
                    return Objects.hash(getName(), getAge()         // IDEA 自动生成的hashCode方法
                }
            }
    b.问题2
        a.提问
            如何比较两个对象内容是否相同?(默认equals比较地址没有实际意义,重写equals比较成员变量)
        b.回答
            public class Student {
                public String name;
                public int age;

                @Override
                public boolean equals(Object o) {
                    // 父类Object无法调用子类Student的特有功能,所以使用向下转型
                    Student s = (Student) o;

                    // name为String类型,使用String类中重写的equals方法
                    if (this.name.equals(s.name) && this.age == s.age) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
            -------------------------------------------------------------------------------------------
            public class Student {
                public String name;
                public int age;

                @Override
                public boolean equals(Object o) {
                    // 对象相等 -> hashcode一定相同 √
                    if (this == o) {
                        return true;
                    }

                    // 测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型
                    if (!(o instanceof Student)) {
                        return false;
                    }

                    Student student = (Student) o;
                    return this.name.equals(student.name) && this.age == student.age;
                }
            }
            -------------------------------------------------------------------------------------------
            public class Student {
                public String name;
                public int age;

                @Override
                public boolean equals(Object o) {       // IDEA 自动生成的equals方法
                    if (this == o) return true;
                    if (!(o instanceof Student)) return false;
                    Student student = (Student) o;
                    return Objects.equals(this.name, student.getName) && this.age == student.age;
                }
            }
    c.问题3
        a.提问1
            什么是 hashCode() 和 equals()?
        b.回答1
            hashCode()方法是Object类中的一个本地方法,会返回该对象的int类型的哈希码,也称为散列码;
            作用是确定该对象在哈希表中的索引位置,根据“键”快速的检索出对应的“值”,从而判断是不是同一个对象;
            -------------------------------------------------------------------------------------------
            equals()方法是Object类中的一个方法,如果没有对equals方法进行重写,则比较的是栈中的值(原生/引用类型);
            一般会选择重写此方法,来比较两个对象的内容是否相等,相等则返回true
        c.提问2
            为什么重写 equals 时必须重写 hashCode 方法?
        d.回答2
            如果重写 equals 后,而未重写 hashCode,可能会出现equals相同(根据对象的特征进行重写),而hashCode不同
            public class Student {
                private String name;
                public int age;
                // get set ...
                // 重写 equals() 不重写 hashcode()

                public static void main(String[] args) {
                    Student stu1 = new Student("BWH_Steven", 22);
                    Student stu2 = new Student("BWH_Steven", 22);
                    System.out.println(stu1.equals(stu2));          // 结果为:true
                    System.out.println(stu1.hashCode());            // 结果为:1128032093
                    System.out.println(stu2.hashCode());            // 结果为:1066516207
                }
            }
        e.提问3
            equals() 已经实现功能了,还需要 hashCode() 做什么?
        f.回答3
            重写equals(),虽然过程详细,但效率较低,例如集合中目前2000个元素,加入第2001元素时,调用equals方法2000次
            hashCode(),哈希算法,效率更快
        g.提问4
            为什么不全部使用高效率的 hashCode(),还要用 equals()?
        h.回答4
            hash相同的对象 -> 对象可能相同、也可能不同 √
            hashCode()方法不是一个100%可靠的方法,个别情况下,不同的对象生成的hashcode也可能会相同
        i.提问5
            hashCode() 和 equals() 是如何一起判断保证高效又可靠的?
        j.回答5
            如果把对象保存到 HashTable、HashMap、HashSet 等中(不允许重复),
            首先,使用hashCode()去比较,如果【二者hash不同的对象 -> 对象一定不同 √】
            然后,为保证其绝对可靠,再使用equals()进行再次内容比较,从而准确判断二者是否相同
            如果hashCode()方法结果不同,则两个对象肯定不同,则无需再使用equals()方法验证

1.3 Objects

01.介绍
	a.compare
		public static <T> int compare(T a, T b, Comparator<? super T> c) 
		使用指定的比较器c 比较参数a和参数b的大小(相等返回0,a大于b返回整数,a小于b返回负数)
	b.equals
		public static boolean equals(Object a, Object b);
		比较两个对象是否相等(首先比较内存地址,然后比较a.equals(b),只要符合其中之一返回true)
		-----------------------------------------------------------------------------------------------------
		public static boolean deepEquals(Object a, Object b);
		深度比较两个对象是否相等(首先比较内存地址,相同返回true;如果传入的是数组,则比较数组内的各个值是否相同)
	c.hash
		public static int hash(Object... values);
		返回传入可变参数的所有值的hashCode的汇总
		-----------------------------------------------------------------------------------------------------
		public static int hashCode(Object o);
		返回对象的hashCode,若传入的为null,返回0
	d.requireNonNull
        public static <T> T requireNonNull(T obj) 
        如果传入的obj为null抛出NullPointerException,否者返回obj
        -----------------------------------------------------------------------------------------------------
        public static <T> T requireNonNull(T obj, String message) 
        如果传入的obj为null抛出NullPointerException并可以指定错误信息message,否者返回obj
	e.toString
        public static String toString(Object o)
        返回对象的String表示,若传入null,返回null字符串
		-----------------------------------------------------------------------------------------------------
        public static String toString(Object o, String nullDefault)
        返回对象的String表示,若传入null,返回默认值nullDefault

02.源码
    public final class Objects {                                          // 排null,调用安全的方法
        private Objects() {
            throw new AssertionError("No java.util.Objects instances for you!");
        }

        public static boolean equals(Object a, Object b) {                // 排null,调用Object的equals()
            return (a == b) || (a != null && a.equals(b));
        }

        public static boolean deepEquals(Object a, Object b) {            // 排null,调用Arrays.deepEquals0()
            if (a == b)
                return true;
            else if (a == null || b == null)
                return false;
            else
                return Arrays.deepEquals0(a, b);
        }

        public static int hashCode(Object o) {                            // 排null,返回其hashCode
            return o != null ? o.hashCode() : 0;
        }

        public static int hash(Object... values) {                        // 调用Arrays.hashCode()
            return Arrays.hashCo    	de(values);
        }

        public static String toString(Object o) {                         // 调用String.valueOf()转换String类型
            return String.valueOf(o);
        }

        public static String toString(Object o, String nullDefault) {     // 排null,调用Object的toString()
            return (o != null) ? o.toString() : nullDefault;
        }

        public static <T> int compare(T a, T b, Comparator<? super T> c) {// 判断a==b,并指定Comparator比较器
            return (a == b) ? 0 :  c.compare(a, b);
        }

        public static <T> T requireNonNull(T obj) {                       // 检查,对象引用不是null
            if (obj == null)
                throw new NullPointerException();
            return obj;
        }

        public static <T> T requireNonNull(T obj, String message) {       // 检查,对象引用不是null,抛自定义msg
            if (obj == null)
                throw new NullPointerException(message);
            return obj;
        }

        public static boolean isNull(Object obj) {                        // 判断,是否为“是null”
            return obj == null;
        }

        public static boolean nonNull(Object obj) {                       // 判断,是否为“非null”
            return obj != null;
        }

        public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
            if (obj == null)
                throw new NullPointerException(messageSupplier.get());
            return obj;
    }

2 part02

2.1 Math

01.Practice
    a.n位随机整数
        a.代码
            // random:[0~1)
            double ran = Math.random();
            System.out.println(ran);

            // 三位随机整数:[0~1)*900=[0~900) -> int强制转换为[0~899] -> [0~899]+100=[0~999]
            System.out.println( (int)(Math.random() * 900) + 100);

            // 四位随机整数:[0~1)*9000=[0~9000) -> int强制转换为[0~8999] -> [0~8999]+1000=[0~9999]
            System.out.println( (int)(Math.random() * 9000) + 1000);
        b.分析
            n位随机整数:(int)(Math.random() * [n位最大值 - n位最小值] + n位最小值)
        c.练习
            a.代码
                // 3位随机整数
                int vip = (int)(Math.random()*900) + 100;
                int shiwei = vip/10%10;

                // [0~1)*10 -> int强制转换为[0-9]
                int ran = (int)(Math.random*10);

                // 中奖规则
                if (shiwei == ran) {
                    System.out.printLn("中奖");
                } else {
                    System.out.printLn("没中奖");
                }
                System.out.printLn("幸运卡号:" + vip + ",幸运随机数:" + ran);
            b.分析
                三位数的会员卡号,必须【(shiwei == ran】,则【中奖】
    b.数字某X位(水仙花数)
        a.代码
            int gewei = num / 1 % 10;       // 12345 -> 12345 -> 5
            int shiwei = num / 10 % 10;     // 12345 -> 1234  -> 4
            int baiwei = num / 100 % 10;    // 12345 -> 123   -> 3
            int qianwei = num / 1000 % 10;  // 12345 -> 12    -> 2
        b.分析
            数字某X位 = num / X位的最小值 % 10
    c.两数交换
        a.代码
            int a = 10;
            int b = 20;
        b.中间变量
            int temp = a;
            a = b;
            b = temp;
        c.数字加减:若数字过大,会导致溢出
            a = a + b;
            b = a - b;
            a = a - b;
        d.位运算:效率最高
            a = a ^ b;
            b = a ^ b;
            a = a ^ b;

2.2 Scanner

01.Scanner类
    a.Java中Scanner类中的方法:next()、nextLint()都会【读入控制台的字符】,区别如下:
        a.next()/nextInt()
            不会读入【字符前后的空格/TAB】,只读入【字符】
            【开始读入字符,遇到"空格/TAB/字符"截止(字符前后的空格/TAB/字符,不算数)】
        b.nextLine()
            会读入【字符前后的空格/TAB】,也会读入【字符】
            【开始读入空格/TAB/字符,遇到"回车"截止】
    b.举例一
        a.代码
            Scanner input = new Scanner(System.in);       |
            System.out.println("请输入成绩:");            | 请输入成绩:
            int javaScore = input.nextInt();              | 100
            System.out.println("请输入姓名:");            | 请输入姓名:
            String name = input.nextLint();               | 输入姓名是:◡
            System.out.println("输入姓名是:" + name);     |
        b.分析
            输入"10"后,【回车】,但"nextInt()不接收回车";
            但,"回车被nextLint()接收,直接执行下一条语句,即System.out.println("输入姓名是:" + name); "
    c.举例二
        a.代码
            Scanner input = new Scanner(System.in);       |
            System.out.println("请输入姓名:");            | 请输入姓名:
            String name = input.nextLint();               | 我是 张三
            System.out.println("输入姓名是:" + name);     | 请输入姓名:我是
            System.out.println("请输入成绩:");            | 请输入成绩:
            int javaScore = input.nextInt();              | Exception in thread "main" java.util.Input
        b.分析
            输入"我是 张三"后,【回车】,"nextLint()遇到回车截止,但会读入空格/TAB/字符"
            同时,"next()无法识别空格,错把"空格"当作结束,而在输出流当中,仍存在"张三"被nextInt()接收,导致错误"
    d.举例三
        a.代码1
            System.out.println("-----------");            |
            System.out.println();                         | 空语句,先执行()内,再进行回车
            System.out.println("-----------");            |
        b.代码2
            System.out.print("*************");            | 等同于 System.out.println("*************");
            System.out.print("\n");
        b.代码3
    e.举例四
        a.代码
            System.out.println(3 * 0.3);                  | 结果:0.8999999999999999   int自动转换为double
            System.out.println(3 * 0.3d);                 | 结果:0.8999999999999999   int自动转换为double
            System.out.println(3 * 0.3f);                 | 结果:0.90000004           int自动转换为float
        b.分析
            a.理解1
                int = 32bits = 4字节    容纳2^32的数字
                float = 32bits = 4字节  无穷无尽的小数
                double = 64bits = 8字节 无穷无尽的小数     int自动转换为float,【2^32】无法容纳【无穷无尽的小数】
            b.理解2
                二进制  整数   5 = 2^0 + 2^2   17 = 2^0 + 2^4
                二进制  小数 0.6 = 2^-1

2.3 BigDecimal、BigInteger

01.小数计算问题
    a.代码
        System.out.println(0.1 + 0.2);          // 0.30000000000000004
        System.out.println(1.0 - 0.8);          // 0.19999999999999996
        System.out.println(4.015 * 100);        // 401.49999999999994
        System.out.println(123.3 / 100);        // 1.2329999999999999
        double amount1 = 2.15;
        double amount2 = 1.10;
        System.out.println(amount1 - amount2);  // 1.0499999999999998
    b.分析
        计算机是以二进制存储数值的,浮点数也不例外。Java采用了IEEE754标准实现浮点数的表达和运算。
        比如,0.1的二进制表示为0.0 0011 0011 0011… (0011 无限循环)
        再转换为十进制就是 0.1000000000000000055511151231257827021181583404541015625,这对0.1无法精确表达
    c.BigDecimal
        a.代码
            BigDecimal bd = new BigDecimal(0.1).add(new BigDecimal(0.2));
            System.out.println(bd);             // 0.3000000000000000166533453693773481063544750213623046875
        b.分析
            结果不符合预期
            使用 BigDecimal 表示和计算浮点数,且务必【使用字符串的构造方法来初始化BigDecimal】,【正确示例如下】:
            String s = String.valueOf(0.1d);
            String t = String.valueOf(0.2d);
            BigDecimal st = new BigDecimal(s).add(new BigDecimal(t));   // 0.3 符合预期
            System.out.println(st);

02.小数精度和格式化
    a.代码
        double num1 = 3.35;
        float num2 = 3.35f;

        System.out.println(num1);                                       // 3.35  错误示例:默认输出
        System.out.println(num2);                                       // 3.35  错误示例:默认输出

        System.out.println(String.format("%.1f", num1));                // 3.4   错误示例:String.format
        System.out.println(String.format("%.1f", num2));                // 3.3   错误示例:String.format

        DecimalFormat df = new DecimalFormat("#.##");
        format.setRoundingMode(RoundingMode.DOWN);
        System.out.println(df.format(num1));                            // 3.35  错误示例:DecimalFormat.format

        format.setRoundingMode(RoundingMode.DOWN);
        System.out.println(df.format(num2));                            // 3.34  错误示例:DecimalFormat.format

        BigDecimal n1 = new BigDecimal(num1);
        BigDecimal n1_down = n1.setScale(1, BigDecimal.ROUND_DOWN);
        System.out.println(n1_down);                                    // 3.3   正确示例:BigDecimal.setScale

        BigDecimal n1_up = n1.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println(n1_up);                                      // 3.4   正确示例:BigDecimal.setScale
    b.分析
        浮点数的字符串,使用BigDecimal格式化

03.BigDecimal判等问题
    a.示例1
        a.代码
            private static void wrong() {
                System.out.println(new BigDecimal("1.0").equals(new BigDecimal("1")));      // false
            }
        b.分析
            BigDecimal的equals方法:比较BigDecimal的【value和scale】,则【1.0 的 scale 是 1】、【1 的 scale 是 0】
    b.示例2
        a.代码
            private static void right() {
                System.out.println(new BigDecimal("1.0").compareTo(new BigDecimal("1")) == 0);
            }
        b.分析
            若只比较 BigDecimal 的 value,则使用 compareTo 方法

04.数值溢出
    a.代码
        long l = Long.MAX_VALUE;
        System.out.println(l + 1);                   // -9223372036854775808  数值溢出,无溢出,容易被忽略
        System.out.println(l + 1 == Long.MIN_VALUE); // true                  数值溢出,无溢出,容易被忽略
    b.分析
        a.方法1:使用 Math 类的 addExact、subtractExact 等 xxExact 方法进行数值运算,会主动抛出异常
            try {
                long l = Long.MAX_VALUE;
                System.out.println(Math.addExact(l, 1));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        b.方法2:使用 BigInteger 类,解决科学计算问题
            BigInteger i = new BigInteger(String.valueOf(Long.MAX_VALUE));
            System.out.println(i.add(BigInteger.ONE).toString());
            try {
                long l = i.add(BigInteger.ONE).longValueExact();
            } catch (Exception ex) {
                ex.printStackTrace();
            }

3 java.lang

3.1 Comparable、Comparator接口

01.Comparable接口
    a.源码
        package java.lang;

        public interface Comparable<T> {
            public int compareTo(T o);
        }
    b.解读
        【实现Comparable接口的类,按照compareTo(T o)比较,这种排列顺序称为“自然顺序”(实体类实现)】
        【内部比较器:Comparable是排序接口,只要实现接口的对象直接就成为一个可以比较的对象,但是需要修改源代码】
        ---------------------------------------------------------------------------------------------------
        只实现compareTo(T o)方法
        compareTo 方法的返回值有三种情况:
            e1.compareTo(e2) > 0,返回正整数,即 e1 > e2,倒序
            e1.compareTo(e2) = 0,返回0    ,即 e1 = e2
            e1.compareTo(e2) < 0,返回负整数,即 e1 < e2,升序
    c.使用方式
        Byte、Short、Integer、Long类:默认已经实现compareTo接口,直接按照“内置规则”比较大小
        Float、Double类、Character、Boolean类:默认已经实现compareTo接口,直接按照“内置规则”比较大小
        public int compareTo(String anotherString) {
            int len1 = value.length;
            int len2 = anotherString.value.length;
            int lim = Math.min(len1, len2);
            char v1[] = value;
            char v2[] = anotherString.value;

            int k = 0;
            while (k < lim) {
                char c1 = v1[k];
                char c2 = v2[k];
                if (c1 != c2) {
                    return c1 - c2;
                }
                k++;
            }
            return len1 - len2;
        }
        -----------------------------------------------------------------------------------------------------
        ArrayList、LinkedList、Vector父类->Stack:重写List接口(非Set接口)中的sort方法,直接按照“内置规则”比较大小
        default void sort(Comparator<? super E> c) {
            Object[] a = this.toArray();
            Arrays.sort(a, (Comparator) c);
            ListIterator<E> i = this.listIterator();
            for (Object e : a) {
                i.next();
                i.set((E) e);
            }
        }
        -----------------------------------------------------------------------------------------------------
        实现Comparable接口的【类的对象】的【列表】,可以通过【java.util.Collections.sort】自动排序
        public static <T extends Comparable<? super T>> void sort(List<T> list)
        public static <T> void sort(List<T> list, Comparator<? super T> c)
        -----------------------------------------------------------------------------------------------------
        实现Comparable接口的【类的对象】的【数组】,可以通过【java.util.Arrays.sort】自动排序
        public static void sort(byte[] a)
        public static void sort(short[] a)
        public static void sort(int[] a)
        public static void sort(long[] a)
        public static void sort(float[] a)
        public static void sort(double[] a)
        public static void sort(char[] a)
        public static void sort(Object[] a)
        public static void sort(Object[] a, int fromIndex, int toIndex)
        public static <T> void sort(T[] a, Comparator<? super T> c)
        public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)
    d.示例
        a.代码
            public class Person implements Comparable<Person>{
                String name;
                int age;

                public Person(String name, int age){
                    this.name = name;
                    this.age = age;
                }

                public String getName(){
                    return name;
                }

                public int getAge(){
                    return age;
                }

                @Override
                public int compareTo(Person p) {                // 实现compareTo()方法,并【自定义排序规则】
                    return this.age-p.getAge();                 // e1.compareTo(e2) > 0 或 = 0 或 < 0
                }

                public static void main(String[] args) {
                    Person[] people = new Person[]{new Person("xujian", 20), new Person("xiewei", 10)};
                    System.out.print("排序前:");
                    for (Person person : people) {
                        System.out.print(person.getName() + ":" + person.getAge() + ",");
                    }
                    Arrays.sort(people);                        // 调用 Arrays.sort 自动排序
                    System.out.print("\n排序后:");
                    for (Person person : people) {
                        System.out.print(person.getName() + ":" + person.getAge() + ",");
                    }
                }
            }
        b.结果
            排序前:xujian:20,xiewei:10,
            排序后:xiewei:10,xujian:20,

02.Comparator接口:一种策略模式(不改变对象自身),而用一个策略对象来改变它的行为
    a.源码
        package java.util;

        @FunctionalInterface
        public interface Comparator<T> {
            int compare(T o1, T o2);               // @FunctionalInterface保证仅有1个抽象方法(public abstract)

            public boolean equals(Object object);  // 并非抽象类方法(public boolean)

            default Comparator<T> reversed() {                                              // JDK8,默认方法
                return Collections.reverseOrder(this);
            }

            public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {  // JDK8,静态方法
                return new Comparators.NullComparator<>(true, comparator);
            }
        }
    b.解读
        【实现Comparator接口的类,按照compare(T o1,T o2)比较,这种排列顺序称为“定制顺序”(无法修改实现类,调用)】
        【外部比较器:Comparator是比较器,实现该接口后该类本身不支持排序,但可以“自定义比较器”,无需修改源代码】
        ---------------------------------------------------------------------------------------------------
        一定要实现compare(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数
            e1.compare(e2) > 0,返回正整数,即 e1 > e2,倒序
            e1.compare(e2) = 0,返回0    ,即 e1 = e2
            e1.compare(e2) < 0,返回负整数,即 e1 < e2,升序
    c.使用方式
        实现Comparator接口的【类的对象】的【列表】,可以通过【java.util.Collections.sort】自动排序
        public static <T> void sort(List<T> list, Comparator<? super T> c)
        -----------------------------------------------------------------------------------------------------
        实现Comparator接口的【类的对象】的【数组】,可以通过【java.util.Arrays.sort】自动排序
        public static <T> void sort(T[] a, Comparator<? super T> c)
        public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)
    d.示例
        a.代码
            public class PersonCompartor implements Comparator<Person> {
                @Override
                public int compare(Person o1, Person o2) {
                    return o1.getAge() - o2.getAge();
                }
            }
            public class Person {
                String name;
                int age;

                public Person(String name, int age) {
                    super();
                    this.name = name;
                    this.age = age;
                }

                public String getName() {
                    return name;
                }

                public int getAge() {
                    return age;
                }

                public static void main(String[] args) {
                    Person[] people = new Person[]{new Person("xujian", 20), new Person("xiewei", 10)};
                    System.out.print("排序前:");
                    for (Person person : people) {
                        System.out.print(person.getName() + ":" + person.getAge() + ",");
                    }
                    // 调用 Arrays.sort 排序,并指定 自定义的比较器(实现Comparator接口中的compare()方法)
                    Arrays.sort(people, new PersonCompartor());
                    System.out.print("\n排序后:");
                    for (Person person : people) {
                        System.out.print(person.getName() + ":" + person.getAge() + ",");
                    }
                }
            }
        b.结果
            排序前:xujian:20,xiewei:10,
            排序后:xiewei:10,xujian:20,

3.2 Number抽象类

01.源码
    public abstract class Number implements java.io.Serializable {
        public abstract int intValue();
        public abstract long longValue();
        public abstract float floatValue();
        public abstract double doubleValue();

        public byte byteValue() {
            return (byte)intValue();
        }
        public short shortValue() {
            return (short)intValue();
        }

        private static final long serialVersionUID = -8742448824652078965L;
    }

02.解读
    抽象类Number包含了六个【包装类】:byte,double,float,int,long,short
    Number的子类:重写byteVaule、shortValut,实现intVaule、longValue、floatValue、doubleValue

3.3 Integer

01.IntegerCache内部类:缓存机制
    a.基本类型对应的缓冲池
        ByteCache       -128~127
        ShortCache      -128~127
        IntegerCache    -128~127
        LongCache       -128~127
        Character类     \u0000 to \u007F
    b.代码
        // 在 Java 8 中,Integer 缓存池的大小默认为 -128~127
        // 上界是可调的,通过 -XX:AutoBoxCacheMax=<size> 指定缓冲池上界,初始java.lang.IntegerCache.high系统属性
        private static class IntegerCache {
            static final int low = -128;
            static final int high;
            static final Integer cache[];

            static {
                // high value may be configured by property
                int h = 127;
                String integerCacheHighPropValue =
                    sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
                if (integerCacheHighPropValue != null) {
                    try {
                        int i = parseInt(integerCacheHighPropValue);
                        i = Math.max(i, 127);
                        // Maximum array size is Integer.MAX_VALUE
                        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                    } catch( NumberFormatException nfe) {
                        // If the property cannot be parsed into an int, ignore it.
                    }
                }
                high = h;

                cache = new Integer[(high - low) + 1];
                int j = low;
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);

                // range [-128, 127] must be interned (JLS7 5.1.7)
                assert IntegerCache.high >= 127;
            }

            private IntegerCache() {}
        }
        -----------------------------------------------------------------------------------------------------
        Integer中有一段“动态代码块static{}”,该部分内容会在Integer类被加载的时候就执行;
        也就是说,当Integer被加载时,就新建了-128到127的所有数字并存放在Integer数组cache中;
        当调用valueOf方法时,如果参数值在-127到128之间,则要么直接返回一个已有对象,否则新建一个对象;
    c.装箱与拆箱
        a.API
            自动装箱:底层调用【Integer类 的 静态 valueOf(int i) 方法】
            public final class Integer extends Number implements Comparable<Integer> {
                // 先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容,否则new Integer(i)
                public static Integer valueOf(int i) {
                    if (i >= IntegerCache.low && i <= IntegerCache.high)
                        return IntegerCache.cache[i + (-IntegerCache.low)];
                    return new Integer(i);
                }
            }
            -------------------------------------------------------------------------------------------------
            自动拆箱:底层调用【Integer类 实现Number抽象类的 intValue() 方法】
            public final class Integer extends Number implements Comparable<Integer> {
                @Native public static final int   MIN_VALUE = 0x80000000;
                @Native public static final int   MAX_VALUE = 0x7fffffff;
                private final int value;
                
                // 实现Number抽象类中的intValue()方法
                public int intValue() {
                    return value;
                }
            }
        b.示例1
            Integer x = new Integer(123);
            Integer y = new Integer(123);
            System.out.println(x == y);   // false
            -------------------------------------------------------------------------------------------------
            new Integer(123):每次都会新建一个对象;
        c.示例2
            Integer z = Integer.valueOf(123);
            Integer k = Integer.valueOf(123);
            System.out.println(z == k);   // true
            -------------------------------------------------------------------------------------------------
            Integer.valueOf(123):会使用缓存池中的对象,多次调用会取得同一个对象的引用
        d.示例3
            Integer m = 123;
            Integer n = 123;              // 编译器会转成【Integer n = Integer.valueOf(100);】
            System.out.println(m == n);   // true
            -------------------------------------------------------------------------------------------------
            Integer m = 123:因为在-128~127范围间,自动装箱调用valueOf(),引用相同的对象

02.Comparable接口
    a.介绍
        内部比较器:Comparable是排序接口,只要实现接口的对象直接就成为一个可以比较的对象,但是需要修改源代码
    b.方法
        int compareTo(Object o)                         --把这个字符串和另一个对象比较
    c.实现过程
        public int compareTo(Integer anotherInteger) {
            return compare(this.value, anotherInteger.value);
        }
        public static int compare(int x, int y) {
            return (x < y) ? -1 : ((x == y) ? 0 : 1);
        }
        -----------------------------------------------------------------------------------------------------
        说明:使用Integer的value数值,进行大小比较

03.Number抽象类
    a.源码
        public abstract class Number implements java.io.Serializable {
            public abstract int intValue();
            public abstract long longValue();
            public abstract float floatValue();
            public abstract double doubleValue();
    
            public byte byteValue() {
                return (byte)intValue();
            }
            public short shortValue() {
                return (short)intValue();
            }
    
            private static final long serialVersionUID = -8742448824652078965L;
        }
    b.实现如下
        public long longValue() {
            return (long)value;
        }

        public float floatValue() {
            return (float)value;
        }

        public double doubleValue() {
            return (double)value;
        }

04.Object默认父类
    a.已重写的父类方法
        public int hashCode() {}                        --哈希,相等的对象必须具有相等的哈希码
        public boolean equals(Object anObject) {}       --比较,比较两对象是否相等
        public String toString() {}                     --打印,返回对象的字符串表示形式
    b.未重写的父类方法
        private static native void registerNatives();                      注册
        public final native Class <?> getClass()                           反射,返回一个对象运行时的实例类
        protected native Object clone() throws CloneNotSupportedException; 克隆,创建与该对象的类相同的新对象
        protected void finalize() throws Throwable { }                     回收,堆空间对象没有被栈空间变量引用
        --------------------------------------------------------------------------------------------------
        public final void wait() throws InterruptedException { wait(0); }  导致当前线程等待,notify唤醒/时间已过
        public final native void wait(long timeout) throws InterruptedException;
        public final void wait(long timeout, int nanos) throws InterruptedException { ... }
        public final native void notify();                                 随机唤醒【单个线程】,程序无法控制
        public final native void notifyAll();                              唤醒【所有线程】,各线程再争夺对象的锁

05.Integer类
    a.定义
        public final class Integer extends Number implements Comparable<Integer> {
        -----------------------------------------------------------------------------------------------------
        Integer类不能被继承
        Integer继承Number类,可以调用longValue、floatValue、doubleValue返回对应的类型的值
        Integer类实现了Comparable接口,但Integer对象只能和Integer类型的对象使用compareTo进行比较
    b.私有属性
        private final int value;
        private static final long serialVersionUID = 1360826667806852920L;
        -----------------------------------------------------------------------------------------------------
        value属性:Integer对象中真正保存int值
        public Integer(int value) {
            this.value = value;                                           // 通过该构造方法给value赋值
        }
        value被定义成final类型:一旦被初始化之后,就无法再改变value值
        public class IntegerTest {
            public static void main(String[] args) {
                Integer i = new Integer(10);
                i = 5;
            }
        }
        -----------------------------------------------------------------------------------------------------
        serialVersionUID被static final修饰,与序列化有关
        Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的;
        在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较;
        如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)
    c.公共属性
        public static final int MIN_VALUE = 0x80000000;       // int类型能够表示的最小值
        public static final int MAX_VALUE = 0x7fffffff;       // int类型能够表示的最大值
        public static final int SIZE = 32;                    // 以二进制补码形式表示int值的比特位数
        public static final int BYTES = SIZE / Byte.SIZE;     // 以二进制补码形式表示int值的字节数
        public static final Class<Integer> TYPE = 
              (Class<Integer>) Class.getPrimitiveClass("int");// 基本类型int的Class实例
    d.构造方法
        // 构造一个新分配的 Integer 对象,它表示指定的 int 值
        public Integer(int value) {
            this.value = value;
        }
        // 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值
        public Integer(String s) throws NumberFormatException {
            this.value = parseInt(s, 10);
        }
        -----------------------------------------------------------------------------------------------------
        初始化一个Integer对象时,只能创建一个十进制的整数
    e.Integer valueOf(int i)
        value被定义成final类型:一旦被初始化之后,就无法再改变value值
        public class IntegerTest {
            public static void main(String[] args) {
                Integer i = new Integer(10);
                i = 5;
            }
        }
        -----------------------------------------------------------------------------------------------------
        i=5到底是如何改变i的值的呢?是改变了原有对象i中value的值,还是重新创建了一个新的Integer对象呢?
        对上述代码进行反编译,代码如下:
        public class IntegerTest{
            public IntegerTest(){
            }
            public static void main(String args[]) {
                Integer i = new Integer(10);
                i = Integer.valueOf(5);
            }
        }
        不难发现,i=5操作并没有改变使用Integer i = new Integer(10)创建出来的i中的value属性的值
        而是,编译器会把i=5转成i = Integer.valueOf(5),
        此时根据IntegerCache内部类的缓存机制,要么直接返回一个已有对象,要么新建一个对象,显然返回一个已有对象
        并且可以通过以下验证,代码如下:
        public class IntegerTest {
            public static void main(String[] args) {
                Integer i = new Integer(10);
                i = 5;

                Integer j = 5;
                System.out.println(i == j);  // true
        }

06.String转成Integer/int的方法
    a.汇总
        Integer getInteger(String nm)
        Integer getInteger(String nm, int val)
        Integer getInteger(String nm, Integer val)
        Integer decode(String nm)
        Integer valueOf(String s)                   -> 调用 Integer.valueOf(parseInt(s, 10));
        Integer valueOf(String s, int radix)
        int parseUnsignedInt(String s)
        int parseUnsignedInt(String s, int radix)
        int parseInt(String s)
        int parseInt(String s, int radix)
        -------------------------------------------------------------------------------------------------
        以上所有方法都能实现将String类型的值转成Integer(int)类型;
        可以说,所有将String转成Integer的方法都是基于parseInt方法实现的,上述方法调用方法的顺序如下:
        getInteger(String nm) -> getInteger(nm,null) -> Integer.decode()-> Integer.valueOf()-> parseInt()
        -------------------------------------------------------------------------------------------------
        总结1如下:
            parseInt方法返回的是基本类型int
            其他的方法返回的是Integer
            valueOf(String)方法会调用valueOf(int)方法。
        总结2如下:
            ①只需要返回一个基本类型,而不需要一个对象,直接使用Integert.parseInt("123");
            ②需要一个对象,充分借助IntegerCache缓存,使用valueOf()
            ③如果和进制有关,就用decode
            ④从系统配置中取值,就用getInteger
    b.getInteger
        Integer getInteger(String nm)
        Integer getInteger(String nm, int val)
        Integer getInteger(String nm, Integer val)
        第一个参数被视为系统属性的名称:
            通过 System.getProperty(java.lang.String) 方法可以访问系统属性;
            然后,将该属性的字符串值解释为一个整数值,并返回表示该值的Integer对象;
            Properties props = System.getProperties();
            props.put("hollis.integer.test.key","10000");
            Integer i = Integer.getInteger("hollis.integer.test.key");
            System.out.println(i);                                         // 输出10000
        第二个参数是默认值:
            如果未具有指定名称的属性,或者属性的数字格式不正确,或者指定名称为空或 null,则返回默认值。
            public static Integer getInteger(String nm, Integer val) {
                String v = null;
                try {
                    v = System.getProperty(nm);
                } catch (IllegalArgumentException | NullPointerException e) {
                }
                if (v != null) {
                    try {
                        return Integer.decode(v);
                    } catch (NumberFormatException e) {
                    }
                }
                return val;
            }
            先按照nm作为key从系统配置中取出值,然后调用Integer.decode方法将其转换成整数并返回
    c.decode
        Integer decode(String nm)
        将 String 解码为 Integer。接受十进制、十六进制和八进制数字;
        根据要解码的 String(mn)的形式转成不同进制的数字;
        mn由三部分组成:符号、基数说明符和字符序列,以“—0X123”为例,
            -是符号位,
            0X是基数说明符(0表示八进制,0x,0X,#表示十六进制,什么都不写则表示十进制)
            123是数字字符序列。
        举例如下:
            Integer DecimalI = Integer.decode("+10");
            Integer OctI = Integer.decode("-010");
            Integer HexI = Integer.decode("-0x10");
            Integer HexI1 = Integer.decode("#10");
            System.out.println(DecimalI);             // 10
            System.out.println(OctI);                 // -8
            System.out.println(HexI);                 // -16
            System.out.println(HexI1);                // 16
        decode方法的具体实现也比较简单,首先判断String类型的参数mn是否以(+/—)符号开头;
        然后再依次判断是否以”0x”、“#”、“0”开头,确定基数说明符的值;
        然后将字符串mn进行截取,只保留其中纯数字部分。
        在用截取后的纯数字和基数调用valueOf(String s, int radix)方法并返回其值。
    d.valueOf
        public static Integer valueOf(String s) throws NumberFormatException {
             return Integer.valueOf(parseInt(s, 10));
        }
        public static Integer valueOf(String s, int radix) throws NumberFormatException {
            return Integer.valueOf(parseInt(s,radix));
        }
        返回一个Integer对象;
        如果指定第二个参数radix,将第一个参数为用第二个参数指定的基数表示有符号整数,若无按照十进制处理;
        主要用到了两个方法,parseInt(String s, int radix)和valueOf(int i)方法;
        valueOf方法会检查参数内容是否在-127到128之间,如果是则直接返回,否则才会新建一个对象;
    e.parseInt
        public static int parseInt(String s) throws NumberFormatException {
            return parseInt(s,10);
        }
        public static int parseInt(String s, int radix) throws NumberFormatException
        使用第二个参数指定的基数(如果没指定,则按照十进制处理),将字符串参数解析为有符号的整数。
        除了第一个字符可以是用来表示负值的 ASCII 减号 ‘-‘ (‘\u002D’)外,字符串中的字符必须都是指定基数的数字
        (通过 Character.digit(char, int) 是否返回一个负值确定)。返回得到的整数值。
        举例如下:
            parseInt("0", 10) 返回 0
            parseInt("473", 10) 返回 473
            parseInt("-0", 10) 返回 0
            parseInt("-FF", 16) 返回 -255
            parseInt("1100110", 2) 返回 102
            parseInt("2147483647", 10) 返回 2147483647
            parseInt("-2147483648", 10) 返回 -2147483648
            parseInt("2147483648", 10) 抛出 NumberFormatException
            parseInt("99", 8) 抛出 NumberFormatException
            parseInt("Hollis", 10) 抛出 NumberFormatException
            parseInt("Hollis", 27) 抛出 NumberFormatException
            parseInt("ADMIN", 27) 返回 5586836 
        具体实现方式:
            while (i < len) {
            // Accumulating negatively avoids surprises near MAX_VALUE
            digit = Character.digit(s.charAt(i++),radix);
            if (digit < 0) {
                throw NumberFormatException.forInputString(s);
            }
            if (result < multmin) {
                throw NumberFormatException.forInputString(s);
            }
            result *= radix;
            if (result < limit + digit) {
            throw NumberFormatException.forInputString(s);
            }
            result -= digit;
            }
        “12345”按照十进制转成12345的方法其实就是以下方式:((1*10)+2)*10)+3)*10+4)*10+5 
        依次取出“12345”中的每一个字符并将起转成不同进制int类型,则是Character.digit方法实现的

07.int转成String的方法
    a.汇总
        String toString()
        static String toString(int i)
        static String toString(int i, int radix)
        static String toBinaryString(int i)
        static String toHexString(int i)
        static String toOctalString(int i)
        static String toUnsignedString(int i)
        static String toUnsignedString(int i, int radix)
    b.一般在要使用String的时候,很多人愿意使用如下形式
        代码如下:
        Integer s = new Integer(199);
        System.out.println(s + "");
        -----------------------------------------------------------------------------------------------------
        反编译如下:
        Integer s = new Integer(199);
        System.out.println((new StringBuilder()).append(s).append("").toString());
        -----------------------------------------------------------------------------------------------------
        经过网络有人使用JMH测试,证明此方法效率更高

4 java.lang

4.1 Serializable接口

01.序列化和反序列化
    a.图示
        序列化(java.io.ObjectOutputStream 类的 public final void writeObject(Object obj) throws IOException )
                                                  -------→
        对象的状态(成员变量,不包括类中的静态变量)              byte[]字节数组
                                                  ←-------
        反序列化(java.io.ObjectInputStream 类的 public final Object readObject() throws IOException )
    b.技术选型
        thrift、protobuf                                     --对性能敏感,对开发体验要求不高
        hessian                                              --对开发体验敏感,性能有要求
        jackson、gson、fastjson                              --序列化后的数据有良好的可读性(转为json、xml形式)
    c.原因
        【对象的寿命,随着JVM的停止允许,而丢失状态】
        【有时候需要把在内存中的各种对象的状态(也就是实例变量,不是方法)保存下来,并在需要的时候,再将对象恢复】
        【虽然,我们可以通过各种各样的方法来保存“对象的状态”,但JAVA提供了一种保存对象状态的机制,那就是“序列化”】
        ----------------------------------------------------------------------------------------------------
        【有些对象可能很重要且占用不少内存,但“可能暂时不使用该对象”,若直接放入内存显然浪费、若丢弃又需要额外创建对象】
        【“一种折中的方法”,将“对象的状态”暂时放入“文件”、“数据库”,然后根据需要进行“磁盘读取”,这就是“序列化”】
    d.序列化的优点
        【保存到“文件”“数据库”】:将一个已经实例化的类转成文件存储,【隔一段时间后】,可以恢复【类的所有变量和状态】
        【方便在网络中进行传送】:对象、文件、数据等格式,【序列化后,无论原来是什么,都可以变为byte[]字节流】
        【RMI(远程方法的调用)】:分布式对象,利用【对象序列化】运行远程主机上的服务,就像【在本地机上运行对象】一样
    e.Java序列化算法
        所有保存到磁盘的对象都有一个序列化编码号。
        当程序试图序列化一个对象时,会先检查此对象是否已经序列化过。
        若对象从未被序列化过,才会将此对象序列化为字节序列输出;如果此对象已经序列化过,则直接输出编号即可。
    f.Java序列化的缺陷
        无法跨语言:Java序列化目前只适用于Java语言,若两种不同语言使用“序列化”进行通讯,可能无法完成
        容易被攻击:一个实例能直接从byte[]数组创建,而不经过构造方法,可以攻击“将序列化后的对象传输到程序中反序列化”
        序列化后的流太大:ObjectOutputStream 实现对象转二进制编码,编码后的数组太大,影响存储和传输
        序列化性能太差:速度也是体现序列化性能的重要指标,如果速度过慢,会影响网络通信的效率,从而增加系统的响应时间
        序列化编程限制:需要时刻关注两点,【实现Serializable接口(序列化标志)、serialVersionUID(版本号)】
        过程复杂开销大:若一个对象的“成员变量是容器类对象,而容器中的元素也是'容器对象'”,则序列化过程较复杂,开销也较大
    g.注意事项
        反序列化的顺序与序列化时的顺序一致
        -----------------------------------------------------------------------------------------------------
        由于java序利化算法不会重复序列化同一个对象,只会记录已序列化对象的编号
        如果序列化一个可变对象后,更改了对象内容,再次序列化,并不会再次将此对象转换为字节序列,而只是保存序列化编号
        -----------------------------------------------------------------------------------------------------
        序列化时,只对对象的成员变量进行保存,而不管对象的方法
        当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化
        -----------------------------------------------------------------------------------------------------
        声明为static、transient类型的成员数据不能被序列化,因为static代表类的状态,transient代表对象的临时数据
        如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存,这是能用“序列化解决深拷贝的重要原因”
        -----------------------------------------------------------------------------------------------------
        如果一个可序列化的类的成员不是基本类型,也不是String,那这个引用类型也必须是可序列化的;否则,此类无法序列化;
        也就是说,被序列化的类必须属于【Enum、Array和实现Serializable接口】中的一种,否则抛出异常
        -----------------------------------------------------------------------------------------------------
        很多基础类已经实现了serializable接口,比如String、ArrayList、HashMap、LinkedList、TreeSet等
        并非所有的对象都可以序列化,一是安全(private在序列化过程中不受保护)、二是资源分配(socket,thread类)
        -----------------------------------------------------------------------------------------------------
        当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口
        当子类实现序列化,而父类没有实现序列化,注意子类序列化时会调用“父类的无参构造函数”,可能会出现默认值int为0
        -----------------------------------------------------------------------------------------------------
        父类是 Serializable,所有子类都可以被序列化
        子类是 Serializable ,父类不是,则子类可以正确序列化,但父类的属性不会被序列化(不报错,数据丢失)
        如果序列化的属性是对象,则这个对象也必须是 Serializable ,否则报错
        -----------------------------------------------------------------------------------------------------
        反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错
        反序列化时,如果 serialVersionUID 被修改,则反序列化会失败

02.Serializable 接口:标识接口,没有方法
    a.序列化类的要求
        被序列化的类必须属于【Enum、Array和实现Serializable接口】中的一种,否则将抛出NotSerializableException异常
    b.默认序列化机制
        【不仅会序列化“当前对象本身”,还会对“其父类的字段”,“该对象引用的其它对象(成员变量是对象类型)”也进行序列化】;
        【注意,此处的“父类、引用的其他对象”,也必须满足序列化要求,即Enum、Array 和 Serializable中一种】
        【不包括类中的静态变量,“序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量”】
    c.serialVersionUID
        serialVersionUID 字段必须是 static final long 类型,建议使用 private 修饰符(仅声明,子类继承该字段无意义)
        数组类不能声明一个明确的 serialVersionUID,因此它们具有默认的计算值,但是数组类没有这种 serialVersionUID 要求
        ----------------------------------------------------------------------------------------------------
        serialVersionUID 是 Java 为【每个序列化类】产生的版本标识。
        它可以用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。
        如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,会抛出InvalidClassException
        ----------------------------------------------------------------------------------------------------
        在某些场合,希望【类的不同版本对序列化兼容】,因此需要确保【类的不同版本具有“相同”的serialVersionUID】
        在某些场合,不希望【类的不同版本对序列化兼容】,因此需要确保【类的不同版本具有“不同”的serialVersionUID】
        ----------------------------------------------------------------------------------------------------
        若没有显式声明 serialVersionUID,【默认 serialVersionUID 值根据类名、接口名、成员方法及属性生成】
        虽然可以生成不同的值,但【不同JDK编译可能生成不同值,会导致异常】,因此建议【给每个类指定serialVersionUID值】
    d.transient
        当某些变量不想被序列化,同是又不适合使用static关键字声明,那么此时就需要用transient关键字来声明该变量
        被transient修饰的属性是默认值:对于引用类型是null;基本类型是0;boolean类型是false
        public class SerializeDemo02 {
            static class Person implements Serializable {
                transient private Integer age = null;
                // 略
            }
        }
        // Output: name: Jack, age: null, sex: MALE
    e.可选的自定义序列化
        writeObject:自定义序列化规则,比如哪些属性、依据的规则
        readObject:自定义反序列化规则,比如哪些属性、依据的规则
        readObjectNoData:使用不同类接收反序列化对象,或序列化流被篡改时,调用readObjectNoData()初始化反序列化对象
    f.更彻底的自定义序列化
        readResolve、writeReplace访问修饰符,可以是private、protected、public
        如果父类重写了这两个方法,子类都需要根据自身需求重写,这显然不是一个好的设计。
        对于final修饰的类重写readResolve,可以是public;非final修饰的类重写readResolve,不建议public,推荐private

03.Externalizable 接口:继承 Serializable 接口,两个抽象方法(“writeExternal()”、"readExternal()")
    a.失效问题
        可序列化类实现 Externalizable 接口之后,基于 Serializable 接口的默认序列化机制就会失效
    b.相比 Serializable 接口
        若直接使用 Externalizable 接口,
        默认【writeExternal、readExternal未作任何处理,则“该序列化行为将不会保存/读取任何一个字段”】
        序列化时,【先调用“该类的无参构造方法,且类型必须为public”创建新对象,然后“将'待保存字段'分别填充到新对象中”】
    c.替代方案
        实现 Serializable 接口,
        并添加 writeObject(ObjectOutputStream out) 与 readObject(ObjectInputStream in) 方法。
        序列化和反序列化过程中会自动回调这两个方法。
    d.单例模式的序列化问题
        单例模式(某个类的创建的唯一性),如果该类进行“序列化”,会出现“单例模式创建的对象,与序列化后的对象,并不相等”,
        因此,为了保证“单例类创建类的唯一性”,需要重写readResolve()方法,直接返回 Person 的单例对象

04.序列化的类
    a.String
        public final class String implements java.io.Serializable,Comparable<String>,CharSequence {
           private static final long serialVersionUID = -6849794470754667710L;
        }
    b.ArrayList、HashMap、LinkedList、TreeSet
        ArrayList   transient Object[] elementData;
        HashMap     transient Node<K,V>[] table;
        LinkedList  transient Node<E> first;
        TreeSet     private transient NavigableMap<E,Object> m;
        但是,ArrayList、HashMap、LinkedList等数据存储字段,修饰符都是transient,但可以正常序列化、反序列化;
        实际上,各个集合类型,对于序列化和反序列化是有单独的实现的,并没有采用虚拟机默认的方式;
    c.ArrayList序列化
        以ArrayList为例,都采用“Serializable + writeObject、readObject”方法,在transient情况下,实现序列化;
        ArrayList序列化和反序列化主要思路:“根据集合中实际存储的元素个数”来进行操作,避免不必要的空间浪费
        (ArrayList的扩容机制,决定了集合中实际存储的元素个数肯定比集合的可容量要小)
        ----------------------------------------------------
        实现 Serializable 接口,
        并添加 writeObject(ObjectOutputStream out) 与 readObject(ObjectInputStream in) 方法。
        序列化和反序列化过程中会自动回调这两个方法。
        ----------------------------------------------------
        public class ArrayList<E> extends AbstractList<E>
                implements List<E>, RandomAccess, Cloneable, java.io.Serializable
        {
            private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
                int expectedModCount = modCount;
                //序列化当前ArrayList中非transient以及非静态字段
                s.defaultWriteObject();
                //序列化数组实际个数
                s.writeInt(size);
                // 逐个取出数组中的值进行序列化
                for (int i=0; i<size; i++) {
                    s.writeObject(elementData[i]);
                }
                //防止在并发的情况下对元素的修改
                if (modCount != expectedModCount) {
                    throw new ConcurrentModificationException();
                }
            }
            private void readObject(java.io.ObjectInputStream s) throws java.io.IOException{
                elementData = EMPTY_ELEMENTDATA;
                // 反序列化非transient以及非静态修饰的字段,其中包含序列化时的数组大小 size
                s.defaultReadObject();
                // 忽略的操作
                s.readInt(); // ignored
                if (size > 0) {
                    // 容量计算
                    int capacity = calculateCapacity(elementData, size);
                    SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
                    //检测是否需要对数组扩容操作
                    ensureCapacityInternal(size);
                    Object[] a = elementData;
                    // 按顺序反序列化数组中的值
                    for (int i=0; i<size; i++) {
                        a[i] = s.readObject();
                    }
                }
            }
        }

05.子类序列化,父类未序列化:父类属性丢失,注意子类序列化时会调用“父类的无参构造函数”
    a.描述
        Java约定:【构建一个对象,必须先有父对象,才有子对象,因此每次创建会调用“父类的无参构造函数”来符合这一设定】
        因此,【序列化时,需要注意“父类的无参构造函数,对变量进行初始化问题;可能会出现默认值int为0,出现null值”】
    b.代码
        a.子类序列化,父类未序列化
            class People{
                int num;
                public People(){}           // 默认的无参构造函数,没有进行初始化
                public People(int num){     // 有参构造函数
                    this.num = num;
                }
                public String toString(){
                    return "num:"+num;
                }
            }
            class Person extends People implements Serializable{
                private static final long serialVersionUID = 1L;
                String name;
                int age;
                public Person(int num,String name,int age){
                    super(num);             // 调用父类中的构造函数
                    this.name = name;
                    this.age = age;
                }
                public String toString(){
                    return super.toString()+"\tname:"+name+"\tage:"+age;
                }
            }
        b.序列化
            Person person = new Person(10,"tom", 22); // 调用带参数的构造函数num=10,name = "tim",age =22
            oos.writeObject(person);                  // 写出对象
        c.反序列化
            Person person = (Person)ois.readObject(); // 反序列化,调用父类中的无参构函数。
            System.out.println(person);               // 结果:num:0   name:tom    age:22
        d.分析结果
            发现由于父类中无参构造函数并没有对num初始化,所以num使用默认值为0

06.成员是引用的序列化:被序列化的类必须属于【Enum、Array和实现Serializable接口】中的一种,否则抛出异常
    a.代码
        public class Teacher {          // 实现Serializable接口

        }
        public class Person implements Serializable {
            public Person(String name, Teacher teacher) {
                this.name = name;
                this.teacher = teacher;
            }

            private Teacher teacher;
            private int age;
            private String name;
            private String sex;
        }
        public class SerializeAndDeserializeTest {
            public static void main(String[] args) throws Exception {
                Teacher teacher = new Teacher();
                Person person = new Person("Lh", teacher);
                person.setName("gacl");
                person.setAge(25);
                person.setSex("男");

                ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("d:/text.dat")));
                oo.writeObject(person);
                oo.close();
            }
        }
    b.分析
        Exception in thread "main" java.io.NotSerializableException: com.java.master.serializable.Teacher
        因为Teacher类的对象是不可序列化的,这导致了Person对象不可序列化。

07.同一对象不会序列化多次:依次将4个对象写入输入流,同一个“流”中,反序列化的顺序与序列化时的顺序一致
    a.代码
        public class WriteTeacher {
            public static void main(String[] args) throws Exception {
                Person person = new Person("路飞", 20);
                Teacher t1 = new Teacher("雷利", person);
                Teacher t2 = new Teacher("红发香克斯", person);

                // 序列化:依次将4个对象写入输入流
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/text.dat");
                oos.writeObject(t1);
                oos.writeObject(t2);
                oos.writeObject(person);
                oos.writeObject(t2);
                }
            }
        }
        public class ReadTeacher {
            public static void main(String[] args) {
                // 反序列化
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/text.dat"));
                Teacher t1 = (Teacher) ois.readObject();
                Teacher t2 = (Teacher) ois.readObject();
                Person p = (Person) ois.readObject();
                Teacher t3 = (Teacher) ois.readObject();

                System.out.println(t1 == t2);                           // false
                System.out.println(t1.getPerson() == p);                // true
                System.out.println(t2.getPerson() == p);                // true

                System.out.println(t2 == t3);                           // true
                System.out.println(t1.getPerson() == t2.getPerson());   // true
            }
        }
    b.分析
        存放顺序如下:
            Person person = new Person("路飞", 20);
            Teacher t1 = new Teacher("雷利", person);
            Teacher t2 = new Teacher("红发香克斯", person);
            oos.writeObject(t1);
            oos.writeObject(t2);
            oos.writeObject(person);
            oos.writeObject(t2);
        取出顺序如下:
            Teacher t1 = (Teacher) ois.readObject();
            Teacher t2 = (Teacher) ois.readObject();
            Person p = (Person) ois.readObject();
            Teacher t3 = (Teacher) ois.readObject();
        同一个“流”中,反序列化的顺序与序列化时的顺序一致:
            t1 == t2                   // false t1、t2分别取出流中的“第一次Teacher、第二次Teacher”,两次对象不同
            t1.getPerson() == p        // true  序列化Teacher时,同时序列化成员变量Person,而同一对象不会序列化多次
            t2.getPerson() == p        // true  序列化Teacher时,同时序列化成员变量Person,而同一对象不会序列化多次
            t2 == t3                   // true  t2、t3分别取出流中的“第二次Teacher、第三次Teacher”,两次对象不同
            t1.getPerson() == t2.getPerson()  // true 序列化Teacher时,同时序列化Person,而同一对象不会序列化多次

08.序列化算法潜在的问题:同一对象不会序列化多次,更改了对象内容,再次序列化,并不会再次将此对象变为字节序列,而只保存编号
    a.代码
        public static void main(String[] args) {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/text.dat"));
            ObjectInputStream ios = new ObjectInputStream(new FileInputStream("d:/text.dat"));

            // 第一次序列化person
            Person person = new Person("猪八戒", 23);
            oos.writeObject(person);
            System.out.println(person);

            // 修改name
            person.setName("孙悟空");
            System.out.println(person);                 // com.java.master.serializable.Person@7a07c5b4

            // 第二次序列化person
            oos.writeObject(person);                    // com.java.master.serializable.Person@7a07c5b4

            // 依次反序列化出p1、p2
            Person p1 = (Person) ios.readObject();
            Person p2 = (Person) ios.readObject();

            System.out.println(p1 == p2);                           // true
            System.out.println(p1.getName().equals(p2.getName()));  // true
        }
    b.分析
        修改内容后,并不会再次将此对象变为字节序列,而只保存编号

09.可选的自定义序列化:writeObject()、readObject()、readObjectNoData()
    a.代码
        public class Person implements Serializable {
           private String name;
           private int age;
           //省略构造方法,get及set方法

           private void writeObject(ObjectOutputStream out) throws IOException {
               //将名字反转写入二进制流
               out.writeObject(new StringBuffer(this.name).reverse());
               out.writeInt(age);
           }

           private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{
               //将读出的字符串反转恢复回来
               this.name = ((StringBuffer)ins.readObject()).reverse().toString();
               this.age = ins.readInt();
           }
        }
    b.分析
        transient修饰,默认【整数:0,小数:0.0,字符:'\u0000',布尔:flase】【引用:null/对象内部默认属性值/""】
        ---------------------------------------------------------------------------------------------------
        private void writeObject(java.io.ObjectOutputStream out) throws IOException;
        private void readObject(java.io.ObjectIutputStream in) throws IOException,ClassNotFoundException;
        private void readObjectNoData() throws ObjectStreamException;
        writeObject:自定义序列化规则,比如哪些属性、依据的规则
        readObject:自定义反序列化规则,比如哪些属性、依据的规则
        readObjectNoData:使用不同类接收反序列化对象,或序列化流被篡改时,调用readObjectNoData()初始化反序列化对象

10.更彻底的自定义序列化:writeReplace()、readResolve()
    a.代码
        public class Person implements Serializable {
          private String name;
          private int age;
          // 省略构造方法,get及set方法

          private Object writeReplace() throws ObjectStreamException {
              ArrayList<Object> list = new ArrayList<>(2);
              list.add(this.name);
              list.add(this.age);
              return list;
          }

           public static void main(String[] args) throws Exception {
              try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
                   ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
                  Person person = new Person("zs", 23);
                  oos.writeObject(person);

                  ArrayList list = (ArrayList)ios.readObject();
                  System.out.println(list);                                     // [zs, 23]
              }
          }
        }
        public class Person implements Serializable {
            private String name;
            private int age;
            //省略构造方法,get及set方法
             private Object readResolve() throws ObjectStreamException{
                return new ("brady", 23);
            }
            public static void main(String[] args) throws Exception {
                try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
                     ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
                    Person person = new Person("zs", 23);
                    oos.writeObject(person);

                    HashMap map = (HashMap)ios.readObject();                    // {brady=23}
                    System.out.println(map);
                }
            }
        }
    b.分析
        writeReplace():此方法可将任意对象代替目标序列化对象(在序列化时,先writeReplace,后writeObject)
        readResolve():此方法可将任意对象代替目标反序列化对象(在反序列化时,先readeObject,后readResolve)
        ---------------------------------------------------------------------------------------------------
        readResolve、writeReplace访问修饰符,可以是private、protected、public
        如果父类重写了这两个方法,子类都需要根据自身需求重写,这显然不是一个好的设计。
        对于final修饰的类重写readResolve,可以是public;非final修饰的类重写readResolve,不建议public,推荐private

11.Java 序列化
    a.Serializable 接口
        public class SerializeDemo01 {
            enum Sex {
                MALE,
                FEMALE
            }

            static class Person implements Serializable {
                private static final long serialVersionUID = 1L;
                private String name = null;
                private Integer age = null;
                private Sex sex;

                public Person() { }

                public Person(String name, Integer age, Sex sex) {
                    this.name = name;
                    this.age = age;
                    this.sex = sex;
                }

                @Override
                public String toString() {
                    return "Person{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}';
                }
            }

            /**
             * 序列化
             */
            private static void serialize(String filename) throws IOException {
                File f = new File(filename); // 定义保存路径
                OutputStream out = new FileOutputStream(f); // 文件输出流
                ObjectOutputStream oos = new ObjectOutputStream(out); // 对象输出流
                oos.writeObject(new Person("Jack", 30, Sex.MALE)); // 保存对象
                oos.close();
                out.close();
            }

            /**
             * 反序列化
             */
            private static void deserialize(String filename) throws IOException, ClassNotFoundException {
                File f = new File(filename); // 定义保存路径
                InputStream in = new FileInputStream(f); // 文件输入流
                ObjectInputStream ois = new ObjectInputStream(in); // 对象输入流
                Object obj = ois.readObject(); // 读取对象
                ois.close();
                in.close();
                System.out.println(obj);
            }

            public static void main(String[] args) throws IOException, ClassNotFoundException {
                final String filename = "d:/text.dat";
                serialize(filename);
                deserialize(filename);
            }
        }
        // Output:
        // Person{name='Jack', age=30, sex=MALE}
    b.Externalizable 接口
        public class ExternalizeDemo02 {
            enum Sex {
                MALE,
                FEMALE
            }

            static class Person implements Externalizable {
                transient private Integer age = null;
                // 其他内容略

                private void writeObject(ObjectOutputStream out) throws IOException {
                    out.defaultWriteObject();
                    out.writeInt(age);
                }

                private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
                    in.defaultReadObject();
                    age = in.readInt();
                }

                @Override
                public void writeExternal(ObjectOutput out) throws IOException {
                    out.writeObject(name);
                    out.writeInt(age);
                }

                @Override
                public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
                    name = (String) in.readObject();
                    age = in.readInt();
                }
            }
             // 其他内容略
        }
        // Output:
        // call Person()
        // name: Jack, age: 30, sex: null
    c.替代方案:Serializable 接口,添加writeObject(ObjectOutputStream out)、readObject(ObjectInputStream in)
        public class SerializeDemo03 {
            static class Person implements Serializable {
                transient private Integer age = null;
                // 其他内容略

                private void writeObject(ObjectOutputStream out) throws IOException {
                    out.defaultWriteObject();
                    out.writeInt(age);
                }

                private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
                    in.defaultReadObject();
                    age = in.readInt();
                }
                // 其他内容略
            }
            // 其他内容略
        }
    d.单例模式:需要重写readResolve()方法保证“单例模式创建的对象,与序列化后的对象”是同一个,否则会破坏单例原则
        public class SerializeDemo04 {
            enum Sex {
                MALE, FEMALE
            }

            static class Person implements Serializable {
                static final Person instatnce = new Person("Tom", 31, Sex.MALE);

                public static Person getInstance() {
                    return instatnce;
                }

                private void writeObject(ObjectOutputStream out) throws IOException {
                    out.defaultWriteObject();
                    out.writeInt(age);
                }

                // private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
                //     in.defaultReadObject();
                //     age = in.readInt();
                // }

                // 将 readObject 替换为 readResolve 方法,直接返回 Person 的单例对象
                private Object readResolve() {
                    return instatnce;
                }
            }
        }

4.2 AbstractStringBuilder抽象类

01.CharSequence接口:字符序列
    a.介绍
        CharBuffer:用于可修改的固定长度的低级字符序列
        StringBuilder:用于可修改的可变长度字符序列
        CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。
        --------------------------------------------------------------------------------------------------
        CharSequence cs="hello";                        // 创建,正确
        CharSequence cs=new CharSequence("hello");      // 创建,错误
        对于一个抽象类或者是接口类,不能使用NEW关键字来进行赋值,但是可以通过以下的方式来进行实例的创建
        --------------------------------------------------------------------------------------------------
        代表“一个有序字符集合”,定义char值的一个可读可写序列,为不同种类的char序列提供统一的自读访问;
        此接口不修改该equals和hashCode方法的常规协定,因此,通常未定义比较实现 CharSequence 的两个对象的结果;
    b.抽象方法
        int length()                                    --返回此字符序列的长度
        char charAt(int index)                          --返回char指定索引处的值
        CharSequence subSequence(int start, int end)    --返回一个 CharSequence ,这是这个序列的一个子序列
        String toString()                               --以与此顺序相同的顺序返回包含此序列中的字符的字符串
    c.接口的默认方法:使用default关键字
        default IntStream chars()                       --返回一个int的流,从这个序列中零扩展char值
        default IntStream codePoints()                  --从此序列返回码流值

02.Appendable接口:用于字符/字符流拼接,有3个重载的append方法;返回值是自身对象(链式编程)
    a.介绍
        作用:能够添加char序列和值的对象。如果想要接收从Formatter接收格式化输出,那么该类必须实现Appendable接口
        字符:字符应为Unicode字符,且补充字符可以由多个16位char值组成
        线程:线程不一定安全,是否安全是由实现类控制。
        异常:错误(异常)可能传递到调用程序。因为方法本身既有抛出异常:IOException和IndexOutOfBoundsException
    b.抽象方法
        Appendable append(char c)                               --将指定的字符追加到此Appendable
        Appendable append(CharSequence csq)                     --将指定的字符序列追加到此Appendable
        Appendable append(CharSequence csq, int start, int end) --将指定字符序列的子序列追加到此Appendable

03.AbstractStringBuilder抽象类
    a.构造方法
        AbstractStringBuilder() {}
        AbstractStringBuilder(int capacity) { value = new char[capacity]; }
    b.append(10+3=13)
        AbstractStringBuilder append(Object obj)
        AbstractStringBuilder append(String str)
        AbstractStringBuilder append(StringBuffer sb)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder append(char[] str)
        AbstractStringBuilder append(char str[], int offset, int len)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder append(boolean b)
        AbstractStringBuilder append(int i)
        AbstractStringBuilder append(long l)
        AbstractStringBuilder append(float f)
        AbstractStringBuilder append(double d)
    c.insert(12个):在字符串中间插入,类似append,无传入StringBuffer形式
        AbstractStringBuilder insert(int offset, Object obj)
        AbstractStringBuilder insert(int offset, String str)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int offset, char c)
        AbstractStringBuilder insert(int offset, char[] str)
        AbstractStringBuilder insert(int index, char[] str, int offset, int len)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int dstOffset, CharSequence s)
        AbstractStringBuilder insert(int dstOffset, CharSequence s, int start, int end)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int offset, boolean b)
        AbstractStringBuilder insert(int offset, int i)
        AbstractStringBuilder insert(int offset, long l)
        AbstractStringBuilder insert(int offset, float f)
        AbstractStringBuilder insert(int offset, double d)
    d.codepoint(5个)
        int codePointAt(int index)
        int codePointBefore(int index)
        int codePointCount(int beginIndex, int endIndex)
        int offsetByCodePoints(int index, int codePointOffset)
        AbstractStringBuilder appendCodePoint(int codePoint)
    e.长度
        char charAt(int index)
        void setCharAt(int index, char ch)
        void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
        ---------------------------------------------------------------------------------------------------
        char[] value;
        char[] getValue()
        ---------------------------------------------------------------------------------------------------
        int count;
        int length()
        void setLength(int newLength)
        ---------------------------------------------------------------------------------------------------
        public int capacity() { return value.length; }          --length:实际长度,capacity:当前数组可容纳长度
        void ensureCapacity(int minimumCapacity)
        void trimToSize()
    f.其他
        int indexOf(String str)
        int indexOf(String str, int fromIndex)
        int lastIndexOf(String str)
        int lastIndexOf(String str, int fromIndex)
        ---------------------------------------------------------------------------------------------------
        String substring(int start)
        String substring(int start, int end)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder delete(int start, int end)
        AbstractStringBuilder deleteCharAt(int index)
        AbstractStringBuilder reverse()
        AbstractStringBuilder replace(int start, int end, String str)
    g.字段
        int count;                                                              --继承AbstractStringBuilder
        char[] value;                                                           --继承AbstractStringBuilder

4.3 String

01.CaseInsensitiveComparator内部类
    a.介绍
        为String中的compareToIgnoreCase()提供一个比较器(内部类实现)
    b.代码
        public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();

        private static class CaseInsensitiveComparator implements Comparator<String>, java.io.Serializable {
            private static final long serialVersionUID = 8575799808933029326L;

            /** compare将参数s1和s2先转化为char数组,然后指定下标的char来进行比较,大小写各比较一次 */
            public int compare(String s1, String s2) {
                int n1 = s1.length();
                int n2 = s2.length();
                int min = Math.min(n1, n2);
                for (int i = 0; i < min; i++) {
                    char c1 = s1.charAt(i);
                    char c2 = s2.charAt(i);
                    if (c1 != c2) {
                        //转换为大写
                        c1 = Character.toUpperCase(c1);
                        c2 = Character.toUpperCase(c2);
                        if (c1 != c2) {
                            //转化为小写
                            c1 = Character.toLowerCase(c1);
                            c2 = Character.toLowerCase(c2);
                            if (c1 != c2) {
                                // No overflow because of numeric promotion
                                return c1 - c2;
                            }
                        }
                    }
                }
                return n1 - n2;
            }

            /** 替换反序列化的对象 */
            private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
        }

02.CharSequence接口:字符序列
    a.介绍
        CharBuffer:用于可修改的固定长度的低级字符序列
        StringBuilder:用于可修改的可变长度字符序列
        CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。
        --------------------------------------------------------------------------------------------------
        CharSequence cs="hello";                        // 创建,正确
        CharSequence cs=new CharSequence("hello");      // 创建,错误
        对于一个抽象类或者是接口类,不能使用NEW关键字来进行赋值,但是可以通过以下的方式来进行实例的创建
        --------------------------------------------------------------------------------------------------
        代表“一个有序字符集合”,定义char值的一个可读可写序列,为不同种类的char序列提供统一的自读访问;
        此接口不修改该equals和hashCode方法的常规协定,因此,通常未定义比较实现 CharSequence 的两个对象的结果;
    b.抽象方法
        int length()                                    --返回此字符序列的长度
        char charAt(int index)                          --返回char指定索引处的值
        CharSequence subSequence(int start, int end)    --返回一个 CharSequence ,这是这个序列的一个子序列
        String toString()                               --以与此顺序相同的顺序返回包含此序列中的字符的字符串
    c.接口的默认方法:使用default关键字
        default IntStream chars()                       --返回一个int的流,从这个序列中零扩展char值
        default IntStream codePoints()                  --从此序列返回码流值

03.Comparable接口
    a.介绍
        内部比较器:Comparable是排序接口,只要实现接口的对象直接就成为一个可以比较的对象,但是需要修改源代码
    b.方法
        int compareTo(Object o)                         --把这个字符串和另一个对象比较

04.Object默认父类
    a.已重写的父类方法
        public int hashCode() {}                        --哈希,相等的对象必须具有相等的哈希码
        public boolean equals(Object anObject) {}       --比较,比较两对象是否相等
        public String toString() {}                     --打印,返回对象的字符串表示形式
    b.未重写的父类方法
        private static native void registerNatives();                      注册
        public final native Class <?> getClass()                           反射,返回一个对象运行时的实例类
        protected native Object clone() throws CloneNotSupportedException; 克隆,创建与该对象的类相同的新对象
        protected void finalize() throws Throwable { }                     回收,堆空间对象没有被栈空间变量引用
        --------------------------------------------------------------------------------------------------
        public final void wait() throws InterruptedException { wait(0); }  导致当前线程等待,notify唤醒/时间已过
        public final native void wait(long timeout) throws InterruptedException;
        public final void wait(long timeout, int nanos) throws InterruptedException { ... }
        public final native void notify();                                 随机唤醒【单个线程】,程序无法控制
        public final native void notifyAll();                              唤醒【所有线程】,各线程再争夺对象的锁

05.String类
    a.定义
        public final class String
            implements java.io.Serializable, Comparable<String>, CharSequence {
            private final char value[];
        -----------------------------------------------------------------------------------------------------
        final修饰String类,表示【不可继承类】,是指【字符串对象本身不能改变,不等同于“对象的引用不能改变”】
        final修改char[]数组,表示【数组存储于char[]数组】,表示【String对象不可被更改】
    b.构造方法
        String() { this.value = "".value; }                     --构造一个空字符串对象,而非"
        --------------------------------------------------------
        String(byte[] bytes)                                    --通过byte数组构造字符串对象
        String(byte[] bytes, int offset, int length)            --通过byte数组,从offset开始,length长度的字符串
        --------------------------------------------------------
        String(char[] value)                                    --通过char数组构造字符串对象
        String(char[] value, int offset, int count)             --通过char数组,从offset开始,length长度的字符串
        --------------------------------------------------------
        String(String original)                                 --字符串常量池,新创建的字符串是参数字符串的副本
        String(StringBuffer buffer)                             --通过StringBuffer数组构造字符串对象
        String(StringBuilder builder)                           --通过StringBuilder数组构造字符串对象
    c.非静态方法
        int codePointAt(int index)                              --返回指定索引处的字符(Unicode代码点)
        int codePointBefore(int index)                          --返回指定索引之前的字符(Unicode代码点)
        int codePointCount(int beginIndex, int endIndex)        --返回此String指定文本范围内的Unicode代码点数
        int offsetByCodePoints(int index, int codePointOffset)  --返回此String的指数
        ------------------------------------------------------------------------------------------------------
        void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) --将此字符串中的字符复制到目标字符数组
        byte[] getBytes()                                       --将结果存储到新的字节数组中
        byte[] getBytes(Charset charset)                        --将结果存储到新的字节数组中,指定charset编码
        byte[] getBytes(String charsetName)                     --将结果存储到新的字节数组中,指定String编码
        ------------------------------------------------------------------------------------------------------
        boolean contentEquals(CharSequence cs)                  --将此字符串与指定CharSequence进行CharSequence
        boolean contentEquals(StringBuffer sb)                  --将此字符串与指定的StringBuffer进行StringBuffer
        ------------------------------------------------------------------------------------------------------
        boolean equalsIgnoreCase(String anotherString)          --内容数值,此String与其他String比较,忽略大小写
        int compareTo(String anotherString)                     --字典顺序,此String与其他String比较,不忽略
        int compareToIgnoreCase(String str)                     --字典顺序,此String与其他String比较,忽略大小写
        ------------------------------------------------------------------------------------------------------
        boolean regionMatches(int toffset,String other,int ooffset,int len)     --测试两个字符串区域是否相等
        boolean regionMatches(boolean ignoreCase,int toffset,String other,int ooffset,int len)
        ------------------------------------------------------------------------------------------------------
        boolean startsWith(String prefix)                       --测试此字符串是否以指定的前缀开头
        boolean startsWith(String prefix, int toffset)          --测试此字符串是否以指定的前缀开头,指定开始索引
        boolean endsWith(String suffix)                         --测试此字符串是否以指定的后缀结尾
        ------------------------------------------------------------------------------------------------------
        int indexOf(int ch)                                     --指定字符第一次出现的索引
        int indexOf(int ch, int fromIndex)                      --指定字符第一次出现的索引,指定开始的索引位置
        int indexOf(String str)                                 --指定子串第一次出现的索引
        int indexOf(String str, int fromIndex)                  --指定子串第一次出现的索引,指定开始的索引位置
        ------------------------------------------------------------------------------------------------------
        int lastIndexOf(int ch)                                 --指定字符的最后一次出现的索引
        int lastIndexOf(int ch, int fromIndex)                  --指定字符的最后一次出现的索引,指定开始的索引位置
        int lastIndexOf(String str)                             --指定子串最后一次出现的索引
        int lastIndexOf(String str, int fromIndex)              --指定子串最后一次出现的索引,指定开始的索引位置
        ------------------------------------------------------------------------------------------------------
        String substring(int beginIndex)                        --返回一个字符串,该字符串是此字符串的子字符串
        String substring(int beginIndex, int endIndex)          --返回一个字符串,该字符串是此字符串的子字符串
        ------------------------------------------------------------------------------------------------------
        String replace(char oldChar, char newChar)              --用newChar替换oldChar,匹配全部字符串
        String replaceAll(String regex, String replacement)     --指定regex规则,匹配全部字符串
        String replaceFirst(String regex, String replacement)   --指定regex规则,匹配第一个字符串
        ------------------------------------------------------------------------------------------------------
        String concat(String str)                               --将指定的字符串连接到该字符串的末尾
        boolean matches(String regex)                           --使用regex规则,匹配是否成功
        boolean contains(CharSequence s)                        --包含指定的char值,匹配是否成功
        ------------------------------------------------------------------------------------------------------
        String[] split(String regex)                            --指定regex规则,分割字符串
        String[] split(String regex, int limit)                 --指定regex规则,分割字符串
        String trim()                                           --返回一个字符串,并删除任何前导和尾随空格
        String intern()                                         --返回字符串对象的规范表示
        ------------------------------------------------------------------------------------------------------
        String toLowerCase()                                    --转换小写
        String toLowerCase(Locale locale)                       --转换小写,指定locale规则
        String toUpperCase()                                    --转换大写
        String toLowerCase(Locale locale)                       --转换小写,指定locale规则
        ------------------------------------------------------------------------------------------------------
        char[] toCharArray()                                    --将此字符串转换为新的字符数组
        ------------------------------------------------------------------------------------------------------
        boolean isEmpty()                                       --判断字符串是否为空
    d.静态方法
        static String join(CharSequence delimiter, CharSequence... elements)
        static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
        ------------------------------------------------------------------------------------------------------
        static String format(String format, Object... args)     --指定区域,格式字符串和参数
        static String format(Locale l, String format, Object... args)
        ------------------------------------------------------------------------------------------------------
        String.valueOf(Object obj)                              --将 obj变量 转换成 String, 等于 obj.toString()
        String.valueOf(char[] data)                             --将 char数组 转换为 String
        String.valueOf(char[] data, int offset, int count)      --将 char数组 中 data[offset]开始取count个元素
        String.copyValueOf(char data[], int offset, int count)  --相当于 valueOf(char[], int, int)
        String.copyValueOf(char[] data)                         --相当于 valueOf(char[])
        String.valueOf(boolean b)                               --将 boolean变量 转换为 String
        String.valueOf(char c)                                  --将 char变量 转换为 String
        String.valueOf(int i)                                   --将 int变量 转换为 String
        String.valueOf(long l)                                  --将 long变量 转换为 String
        String.valueOf(double d)                                --将 double变量 转换为 String
        String.valueOf(float f)                                 --将 float变量 转换为 String
    e.字段
        private int hash;                                       --默认为0
        private final char value[];                             --不可变字符数组char[]
        private static final long serialVersionUID = -6849794470754667710L;
        private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
        public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();

06.String类-构造方法
    a.参数为String类型
        public String(String original) {                        --字符串常量池,新创建的字符串是参数字符串的副本
            this.value = original.value;
            this.hash = original.hash;
        }
        -----------------------------------------------------------------------------------------------------
        直接将源 String 中的 value 和 hash 两个属性直接赋值给目标 String;
        因为 String 一旦定义之后是不可以改变的,所以也就不用担心改变源 String 的值会影响到目标 String 的值
    b.参数为Char数组
        public String(char value[]) {                           --通过char数组构造字符串对象
            this.value = Arrays.copyOf(value, value.length);
        }
        public String(char value[], int offset, int count) {    --通过char数组,从offset开始,length长度的字符串
            if (offset < 0) {
                throw new StringIndexOutOfBoundsException(offset);
            }
            if (count <= 0) {
                if (count < 0) {
                    throw new StringIndexOutOfBoundsException(count);
                }
                if (offset <= value.length) {
                    this.value = "".value;
                    return;
                }
            }
            // Note: offset or count might be near -1>>>1.
            if (offset > value.length - count) {
                throw new StringIndexOutOfBoundsException(offset + count);
            }
            this.value = Arrays.copyOfRange(value, offset, offset+count);
        }
        -----------------------------------------------------------------------------------------------------
        使用字符数组创建 String 时,用到 Arrays.copyOf 方法或 Arrays.copyOfRange 方法;
        这两个方法是将原有的字符数组中的内容逐一的复制到 String 中的字符数组中,
        会创建一个新的字符串对象,随后修改的字符数组不影响新创建的字符串。
    c.参数为byte数组
        在Java中,String实例中保存有一个char[]字符数组,char[]字符数组是以unicode码来存储的,String和char为内存形式
        byte 是网络传输或存储的序列化形式,所以在很多传输和存储的过程中需要将 byte[] 数组和 String 进行相互转化。
        所以 String 提供了一系列重载的构造方法来将一个字符数组转化成 String,转换时使用【charset指定编码】
        -----------------------------------------------------------------------------------------------------
        String(byte[] bytes, Charset charset)
        该构造方法是指通过charset来解码指定的byte数组,将其解码成unicode的char[]数组,构造成新的String
        这里的 bytes 字节流是使用 charset 进行编码的,想要将他转换成 unicode 的 char[] 数组,需要指定其他解码方式
        -----------------------------------------------------------------------------------------------------
        String(byte bytes[])
        String(byte bytes[], int offset, int length)
        String(byte bytes[], Charset charset)
        String(byte bytes[], String charsetName)
        String(byte bytes[], int offset, int length, Charset charset)
        String(byte bytes[], int offset, int length, String charsetName)
        使用上面构造方法,使用 StringCoding.decode() 解码,使用解码的字符集为指定的charsetName或者charset参数
        -----------------------------------------------------------------------------------------------------
        static char[] decode(byte[] ba, int off, int len){
            String csn = Charset.defaultCharset().name();
            try{ //use char set name decode() variant which provide scaching.
                 return decode(csn, ba, off, len);
            } catch(UnsupportedEncodingException x){
                warnUnsupportedCharset(csn);
            }
        
            try{
               return decode("ISO-8859-1", ba, off, len);  } 
            catch(UnsupportedEncodingException x){
               MessageUtils.err("ISO-8859-1 char set not available: " + x.toString());
               // If we can not find ISO-8859-1 then things are seriously wrong with the installation.
               System.exit(1);
               return null;
            }
        }
        若byte[] 构造 String时,不指定解码使用的字符集,那么StringCoding的decode方法首先调用系统的默认编码格式;
        如果没有指定编码格式则默认使用 ISO-8859-1 编码格式进行编码操作
    d.参数为StringBuilder或StringBuffer
        public String(StringBuffer buffer) {
            synchronized(buffer) {
                this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
            }
        }
        public String(StringBuilder builder) {
            this.value = Arrays.copyOf(builder.getValue(), builder.length());
        }
        -----------------------------------------------------------------------------------------------------
        基本不用,用 StringBuffer.toString() 方法
    e.特殊的protected构造方法
        String(char[] value, boolean share) {
            // assert share : "unshared not supported";
            this.value = value;
        }
        -----------------------------------------------------------------------------------------------------
        从代码中我们可以看出,该方法和 String(char[] value) 有两点区别:
        第一个区别:该方法多了一个参数:boolean share,其实这个参数在方法体中根本没被使用。
                   注释说目前不支持 false,只使用 true。
                   那可以断定,加入这个 share 的只是为了区分于 String(char[] value) 方法,
                   不加这个参数就没办法定义这个函数,只有参数是不同才能进行重载。
        第二个区别:具体的方法实现不同。
                   String(char[] value)方法在创建String时,用Arrays的copyOf方法将value中的内容逐一复制到String 
                   而这个String(char[] value, boolean share)方法则是直接将value的引用赋值给String的value
                   也就是说,这个方法构造出来的String和参数传过来的char[] value共享同一个数组
        -----------------------------------------------------------------------------------------------------
        为什么 Java 会提供这样一个方法呢?
        性能好:这个很简单,一个是直接给数组赋值(相当于直接将 String 的 value 的指针指向char[]数组),
               一个是逐一拷贝,当然是直接赋值快了
        安全的:对于调用他的方法来说,由于无论是原字符串还是新字符串,其 value 数组本身都是 String 对象的私有属性,
               从外部是无法访问的,因此对两个字符串来说都很安全。
        节约内存:该方法之所以设置为 protected,是因为一旦该方法设置为公有,在外面可以访问的话,
                 如果构造方法没有对 arr 进行拷贝,那么其他人就可以在字符串外部修改该数组,
                 由于它们引用的是同一个数组,因此对 arr 的修改就相当于修改了字符串,那就破坏了字符串的不可变性。
        --------------------------------------------------
        在 Java 7 之前有很多 String 里面的方法都使用上面说的那种“性能好的、节约内存的、安全”的构造函数。
        比如:substring,replace,concat,valueOf等方法,实际上它们使用的是 public String(char[], ture) 实现
        但是在 Java 7 中,substring 已经不再使用这种“public String(char[], ture)”的方法了
        --------------------------------------------------
        为什么呢? 虽然这种方法有很多优点,但是有一个致命的缺点,很有可能造成内存泄露。
        新的实现虽然损失了性能,而且浪费了一些存储空间,但却保证了字符串的内部数组可以和字符串对象一起被回收,
        从而防止发生内存泄漏,因此新的 substring 比原来的更健壮。
        --------------------------------------------------
        看一个例子,假设一个方法从某个地方(文件、数据库或网络)取得了一个很长的字符串,
        然后对其进行解析并提取其中的一小段内容,这种情况经常发生在网页抓取或进行日志分析的时候。
        下面是示例代码:
        String aLongString = "...averylongstring...";
        String aPart = data.substring(20, 40);
        return aPart;
        在这里 aLongString 只是临时的,真正有用的是 aPart,其长度只有 20 个字符,但是它的内部数组却是从 aLongString
        那里共享的,因此虽然 aLongString 本身可以被回收,但它的内部数组却不能释放。这就导致了内存泄漏。如果一个程序中
        这种情况经常发生有可能会导致严重的后果,如内存溢出,或性能下降。
        
07.String类-非静态方法
    a.Hashcode()
        public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                char val[] = value;

                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }
        -----------------------------------------------------------------------------------------------------
        hashCode 的实现其实就是使用数学公式:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
    b.equals()
        public boolean equals(Object anObject) {
             if (this == anObject) {
                 return true;
             } 
            if (anObject instanceof String) {
               String anotherString = (String) anObject;
               int n = value.length;
               if (n == anotherString.value.length) {
                   char v1[] = value;
                   char v2[] = anotherString.value;
                   int i = 0;
                   while (n-- != 0) {
                     if (v1[i] != v2[i])
                     return false;
                     i++;
                   }
                   return true;
               }
           } 
           return false;
        }
        -----------------------------------------------------------------------------------------------------
        字符串相同,地址相同,地址不同,但是内容相同,这是一种提高效率的方法;
        也就是,将比较快速的部分(地址,比较对象类型)放在前面比较,速度慢的部分(比较字符数组)放在后面执行;
    c.equalsIgnoreCase()
        public boolean equalsIgnoreCase(String anotherString) {
            return (this == anotherString) ? true : (anotherString != null) && 
                    (anotherString.value.length == value.length) && 
                    regionMatches(true, 0, anotherString, 0, value.length);
        }
        -----------------------------------------------------------------------------------------------------
        多个if语句: 一个三目运算符和 && 操作
    d.intern()
        public native String intern(); 
        -----------------------------------------------------------------------------------------------------
        intern方法是Native调用,它的作用是在方法区中的常量池里寻找等值的对象,
        如果没有找到则在常量池中存放当前字符串的引用并返回该引用,否则直接返回常量池中已存在的String对象引用。

4.4 StringBuffer

01.CharSequence接口:字符序列
    a.介绍
        CharBuffer:用于可修改的固定长度的低级字符序列
        StringBuilder:用于可修改的可变长度字符序列
        CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。
        --------------------------------------------------------------------------------------------------
        CharSequence cs="hello";                        // 创建,正确
        CharSequence cs=new CharSequence("hello");      // 创建,错误
        对于一个抽象类或者是接口类,不能使用NEW关键字来进行赋值,但是可以通过以下的方式来进行实例的创建
        --------------------------------------------------------------------------------------------------
        代表“一个有序字符集合”,定义char值的一个可读可写序列,为不同种类的char序列提供统一的自读访问;
        此接口不修改该equals和hashCode方法的常规协定,因此,通常未定义比较实现 CharSequence 的两个对象的结果;
    b.抽象方法
        int length()                                    --返回此字符序列的长度
        char charAt(int index)                          --返回char指定索引处的值
        CharSequence subSequence(int start, int end)    --返回一个 CharSequence ,这是这个序列的一个子序列
        String toString()                               --以与此顺序相同的顺序返回包含此序列中的字符的字符串
    c.接口的默认方法:使用default关键字
        default IntStream chars()                       --返回一个int的流,从这个序列中零扩展char值
        default IntStream codePoints()                  --从此序列返回码流值

02.AbstractStringBuilder抽象类:实现Appendable, CharSequence接口
    a.构造无法
        无法继承
    b.append(10+3=13)
        AbstractStringBuilder append(Object obj)
        AbstractStringBuilder append(String str)
        AbstractStringBuilder append(StringBuffer sb)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder append(char[] str)
        AbstractStringBuilder append(char str[], int offset, int len)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder append(boolean b)
        AbstractStringBuilder append(int i)
        AbstractStringBuilder append(long l)
        AbstractStringBuilder append(float f)
        AbstractStringBuilder append(double d)
    c.insert(12个):在字符串中间插入,类似append,无传入StringBuffer形式
        AbstractStringBuilder insert(int offset, Object obj)
        AbstractStringBuilder insert(int offset, String str)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int offset, char c)
        AbstractStringBuilder insert(int offset, char[] str)
        AbstractStringBuilder insert(int index, char[] str, int offset, int len)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int dstOffset, CharSequence s)
        AbstractStringBuilder insert(int dstOffset, CharSequence s, int start, int end)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int offset, boolean b)
        AbstractStringBuilder insert(int offset, int i)
        AbstractStringBuilder insert(int offset, long l)
        AbstractStringBuilder insert(int offset, float f)
        AbstractStringBuilder insert(int offset, double d)
    d.codepoint(5个)
        int codePointAt(int index)
        int codePointBefore(int index)
        int codePointCount(int beginIndex, int endIndex)
        int offsetByCodePoints(int index, int codePointOffset)
        AbstractStringBuilder appendCodePoint(int codePoint)
    e.长度
        char charAt(int index)
        void setCharAt(int index, char ch)
        void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
        ---------------------------------------------------------------------------------------------------
        char[] value;
        char[] getValue()
        ---------------------------------------------------------------------------------------------------
        int count;
        int length()
        void setLength(int newLength)
        ---------------------------------------------------------------------------------------------------
        public int capacity() { return value.length; }          --length:实际长度,capacity:当前数组可容纳长度
        void ensureCapacity(int minimumCapacity)
        void trimToSize()
    f.其他
        int indexOf(String str)
        int indexOf(String str, int fromIndex)
        int lastIndexOf(String str)
        int lastIndexOf(String str, int fromIndex)
        ---------------------------------------------------------------------------------------------------
        String substring(int start)
        String substring(int start, int end)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder delete(int start, int end)
        AbstractStringBuilder deleteCharAt(int index)
        AbstractStringBuilder reverse()
        AbstractStringBuilder replace(int start, int end, String str)
    g.字段
        int count;                                                              --继承AbstractStringBuilder
        char[] value;                                                           --继承AbstractStringBuilder

03.StringBuffer类:对本身进行操作,不产生新对象,线程安全,【相比String最大的区别:在原来的内存空间中直接修改变量值】
    a.构造方法
        StringBuffer() { super(16); }                    --构造一个没有字符的字符串缓冲区,容量为16个字符
        StringBuffer(CharSequence seq)                   --构造一个包含与指定的相同字符的字符串缓冲区seq
        StringBuffer(int capacity)                       --构造一个没有字符的字符串缓冲区和指定的初始容量
        StringBuffer(String str)                         --构造一个初始化为指定字符串内容的字符串缓冲区
    b.私有方法
        private synchronized void writeObject(java.io.ObjectOutputStream s)     --序列化
        private void readObject(java.io.ObjectInputStream s)                    --反序列化
    c.字段
        int count;                                                              --继承AbstractStringBuilder
        char[] value;                                                           --继承AbstractStringBuilder
        private transient char[] toStringCache;
        static final long serialVersionUID = 3388685877147921107L;
        private static final java.io.ObjectStreamField[] serialPersistentFields = {...}

4.5 StringBuilder

01.CharSequence接口:字符序列
    a.介绍
        CharBuffer:用于可修改的固定长度的低级字符序列
        StringBuilder:用于可修改的可变长度字符序列
        CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。
        --------------------------------------------------------------------------------------------------
        CharSequence cs="hello";                        // 创建,正确
        CharSequence cs=new CharSequence("hello");      // 创建,错误
        对于一个抽象类或者是接口类,不能使用NEW关键字来进行赋值,但是可以通过以下的方式来进行实例的创建
        --------------------------------------------------------------------------------------------------
        代表“一个有序字符集合”,定义char值的一个可读可写序列,为不同种类的char序列提供统一的自读访问;
        此接口不修改该equals和hashCode方法的常规协定,因此,通常未定义比较实现 CharSequence 的两个对象的结果;
    b.抽象方法
        int length()                                    --返回此字符序列的长度
        char charAt(int index)                          --返回char指定索引处的值
        CharSequence subSequence(int start, int end)    --返回一个 CharSequence ,这是这个序列的一个子序列
        String toString()                               --以与此顺序相同的顺序返回包含此序列中的字符的字符串
    c.接口的默认方法:使用default关键字
        default IntStream chars()                       --返回一个int的流,从这个序列中零扩展char值
        default IntStream codePoints()                  --从此序列返回码流值

02.AbstractStringBuilder抽象类:实现Appendable, CharSequence接口
    a.构造无法
        无法继承
    b.append(10+3=13)
        AbstractStringBuilder append(Object obj)
        AbstractStringBuilder append(String str)
        AbstractStringBuilder append(StringBuffer sb)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder append(char[] str)
        AbstractStringBuilder append(char str[], int offset, int len)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder append(boolean b)
        AbstractStringBuilder append(int i)
        AbstractStringBuilder append(long l)
        AbstractStringBuilder append(float f)
        AbstractStringBuilder append(double d)
    c.insert(12个):在字符串中间插入,类似append,无传入StringBuffer形式
        AbstractStringBuilder insert(int offset, Object obj)
        AbstractStringBuilder insert(int offset, String str)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int offset, char c)
        AbstractStringBuilder insert(int offset, char[] str)
        AbstractStringBuilder insert(int index, char[] str, int offset, int len)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int dstOffset, CharSequence s)
        AbstractStringBuilder insert(int dstOffset, CharSequence s, int start, int end)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder insert(int offset, boolean b)
        AbstractStringBuilder insert(int offset, int i)
        AbstractStringBuilder insert(int offset, long l)
        AbstractStringBuilder insert(int offset, float f)
        AbstractStringBuilder insert(int offset, double d)
    d.codepoint(5个)
        int codePointAt(int index)
        int codePointBefore(int index)
        int codePointCount(int beginIndex, int endIndex)
        int offsetByCodePoints(int index, int codePointOffset)
        AbstractStringBuilder appendCodePoint(int codePoint)
    e.长度
        char charAt(int index)
        void setCharAt(int index, char ch)
        void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
        ---------------------------------------------------------------------------------------------------
        char[] value;
        char[] getValue()
        ---------------------------------------------------------------------------------------------------
        int count;
        int length()
        void setLength(int newLength)
        ---------------------------------------------------------------------------------------------------
        public int capacity() { return value.length; }          --length:实际长度,capacity:当前数组可容纳长度
        void ensureCapacity(int minimumCapacity)
        void trimToSize()
    f.其他
        int indexOf(String str)
        int indexOf(String str, int fromIndex)
        int lastIndexOf(String str)
        int lastIndexOf(String str, int fromIndex)
        ---------------------------------------------------------------------------------------------------
        String substring(int start)
        String substring(int start, int end)
        ---------------------------------------------------------------------------------------------------
        AbstractStringBuilder delete(int start, int end)
        AbstractStringBuilder deleteCharAt(int index)
        AbstractStringBuilder reverse()
        AbstractStringBuilder replace(int start, int end, String str)
    g.字段
        int count;                                                              --继承AbstractStringBuilder
        char[] value;                                                           --继承AbstractStringBuilder

03.StringBuffer类:StringBuffer的等价类,单线程情况下,比StringBuffer速度快
    a.构造方法
        StringBuffer() { super(16); }                      --构造一个没有字符的字符串缓冲区,容量为16个字符
        StringBuilder(CharSequence seq)                    --构造一个包含与指定的相同字符的字符串缓冲区seq
        StringBuilder(int capacity)                        --构造一个没有字符的字符串缓冲区和指定的初始容量
        StringBuilder(String str)                          --构造一个初始化为指定字符串内容的字符串缓冲区
    b.私有方法
        private void writeObject(java.io.ObjectOutputStream s)                  --序列化
        private void readObject(java.io.ObjectInputStream s)                    --反序列化
    c.字段
        int count;                                                              --继承AbstractStringBuilder
        char[] value;                                                           --继承AbstractStringBuilder
        private transient char[] toStringCache;
        static final long serialVersionUID = 3388685877147921107L;
        private static final java.io.ObjectStreamField[] serialPersistentFields = {...}

5 java.util

5.1 Arrays

01.NaturalOrder内部类
    a.介绍
        naturalOrder方法可以为任何实现了Comparable的类建立一个比较器
    b.源码
        static final class NaturalOrder implements Comparator<Object> {
            @SuppressWarnings("unchecked")
            public int compare(Object first, Object second) {
                return ((Comparable<Object>)first).compareTo(second);
            }
            static final NaturalOrder INSTANCE = new NaturalOrder();
        }

02.LegacyMergeSort内部类
    a.介绍
        LegacyMergeSort内部类对引用类型排序的支持
    b.源码
        static final class LegacyMergeSort {
            private static final boolean userRequested =
                java.security.AccessController.doPrivileged(
                    new sun.security.action.GetBooleanAction(
                        "java.util.Arrays.useLegacyMergeSort")).booleanValue();
        }

03.ArrayList内部类
    a.介绍
        调用Arrays.asList(),从asList可知,参数为可变参数,并且支持数组作为参数传入
        此时的list不能进行add操作,否则会报 “java.lang.UnsupportedOperationException”
        Arrays.asList()返回的是List,而且是一个定长的List,所以不能转换为ArrayList,只能转换为AbstractList
        原因在于asList()方法返回的是某个数组的列表形式,返回的列表只是数组的另一个视图,而数组本身并没有消失,
        对列表的任何操作最终都反映在数组上,所以不支持remove()、add()操作
        -----------------------------------------------------------------------------------------------------
        Arrays$ArrayList:不支持增删操作;共享原始数据(Arrays$ArrayList元素与Arrays的数组元素是共享的)
        Arrays#asList返回的ArrayList只是Arrays一个内部类,并非真正的java.util.ArrayList
        java.util.ArrayList和Arrays$ArrayList都继承自AbstractList,而 java.util.Arrays$ArrayList并没有
        重写父类的add/remove方法,而父类方法恰恰都会抛出 UnsupportedOperationException
    b.源码
        private static class ArrayList<E> extends AbstractList<E>
            implements RandomAccess, java.io.Serializable
        {
            private static final long serialVersionUID = -2764017481108945198L;
            private final E[] a;

            ArrayList(E[] array) {
                a = Objects.requireNonNull(array);
            }

            @Override
            public int size() {
                return a.length;
            }

            @Override
            public Object[] toArray() {
                return a.clone();
            }

            @Override
            @SuppressWarnings("unchecked")
            public <T> T[] toArray(T[] a) {
                int size = size();
                if (a.length < size)
                    return Arrays.copyOf(this.a, size,
                                         (Class<? extends T[]>) a.getClass());
                System.arraycopy(this.a, 0, a, 0, size);
                if (a.length > size)
                    a[size] = null;
                return a;
            }

            @Override
            public E get(int index) {
                return a[index];
            }

            @Override
            public E set(int index, E element) {
                E oldValue = a[index];
                a[index] = element;
                return oldValue;
            }

            @Override
            public int indexOf(Object o) {
                E[] a = this.a;
                if (o == null) {
                    for (int i = 0; i < a.length; i++)
                        if (a[i] == null)
                            return i;
                } else {
                    for (int i = 0; i < a.length; i++)
                        if (o.equals(a[i]))
                            return i;
                }
                return -1;
            }

            @Override
            public boolean contains(Object o) {
                return indexOf(o) != -1;
            }

            @Override
            public Spliterator<E> spliterator() {
                return Spliterators.spliterator(a, Spliterator.ORDERED);
            }

            @Override
            public void forEach(Consumer<? super E> action) {
                Objects.requireNonNull(action);
                for (E e : a) {
                    action.accept(e);
                }
            }

            @Override
            public void replaceAll(UnaryOperator<E> operator) {
                Objects.requireNonNull(operator);
                E[] a = this.a;
                for (int i = 0; i < a.length; i++) {
                    a[i] = operator.apply(a[i]);
                }
            }

            @Override
            public void sort(Comparator<? super E> c) {
                Arrays.sort(a, c);
            }
        }

04.java.util.Arrays,所有方法都是static静态方法
    a.定义
        public class Arrays {
        -----------------------------------------------------------------------------------------------------
        只属于java.util下面的一个类,没有任何继承的父类,没有任何实现的接口
        该类包含用于操作数组的各种方法(如排序和搜索),该类还包含一个静态工厂,可以将数组视为列表。 
        如果指定的数组引用为空,则该类中的方法都抛出一个NullPointerException ,除非另有说明。
    b.私有属性
        private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;
        并行排序算法将不会进一步划分排序任务的最小数组长度。
        -----------------------------------------------------------------------------------------------------
        private static final int INSERTIONSORT_THRESHOLD = 7;
        列表大小等于或小于插入排序优先于合并排序。将在未来的版本中删除。
    c.公共属性
        无
    d.构造方法
        private Arrays() {}
        禁止默认构造函数,确保不可实例化。
    e.非静态方法
        无
    f.静态方法
        具体见下一说明

05.java.util.Arrays,所有方法都是static静态方法
    a.打印/输出,转字符串
        public static void main(String[] args) {
            String[] strings = {"a","b","c"};
            System.out.println(strings);                           // [Ljava.lang.String;@36baf30c
            System.out.println(Arrays.toString(strings));          // [a, b, c] 
            System.out.println(Arrays.deepToString(strings));      // [a, b, c] 
        }
        -----------------------------------------------------------------------------------------------------
        Arrays.toString(Object[] array)
        返回一维数组元素的字符串形式
        -----------------------------------------------------------------------------------------------------
        Arrays.deepToString(Object[] array)
        返回多维数组元素的字符串形式
    b.排序,默认升序
        public static void main(String[] args) {
            String[] sorts = {"c","b","a"};
            Arrays.sort(sorts);                                    // 排序,默认升序
            System.out.println(Arrays.toString(sorts));            // [a, b, c] 
        }
        -----------------------------------------------------------------------------------------------------
        对指定对象数组根据其元素的自然顺序进行升序排列,同样的方法适用于所有其他基本数据类型(Byte,short,Int等)
        可通过Arrays.sort()方法对数组进行排序,但对于数组中的元素有一定的要求,要实现Comparable接口。
        为什么String可以直接进行排序?那是因为String已经实现了Comparable接口。
        -----------------------------------------------------------------------------------------------------
        Arrays.sort(Object[] array)
        对数组元素进行排序 (串行排序)
        -----------------------------------------------------------------------------------------------------
        Arrays.sort(T[] array, Comparator<? super T> comparator)
        使用自定义比较器,对数组元素进行排序 (串行排序)
        -----------------------------------------------------------------------------------------------------
        Arrays.sort(Object[] array, int fromIndex, int toIndex)
        对指定范围内的数组元素进行排序 (串行排序)
        -----------------------------------------------------------------------------------------------------
        Arrays.sort(T[] array, int fromIndex, int toIndex, Comparator<? super T> c)
        使用自定义比较器,对指定范围内的数组元素进行排序 (串行排序)
        -----------------------------------------------------------------------------------------------------
        Arrays.parallelSort(T[] array)
        对数组元素进行排序 (并行排序),当数据规模较大时,会有更好的性能
    c.二分查找数组中某元素的下标,查找前必须排序
        public static void main(String[] args) {
            String[] sorts = {"c","a","b"};
            Arrays.sort(sorts);                                    // 二分查找前,必须需要sort排序
            int index = Arrays.binarySearch(sorts,"b");
            System.out.println(index);
        }
        -----------------------------------------------------------------------------------------------------
        Arrays.binarySearch(Object[] array, Object key)
        在调用该方法之前,必须先调用 Arrays.sort() 方法进行排序,如果数组没有排序,那么结果是不确定的,
        此外如果数组中包含多个指定元素,则无法保证将找到哪个元素
        -----------------------------------------------------------------------------------------------------
        Arrays.binarySearch(Object[] array, int fromIndex, int toIndex, Object obj)
        使用二分法查找数组内指定范围内的指定元素的索引值
    d.数组中是否包含某个值
        public static void main(String[] args) {
            String[] array = { "a", "b", "c", "d", "e" };
            boolean b = Arrays.asList(array).contains("a");
            System.out.println(b);
        }
        -----------------------------------------------------------------------------------------------------
        true
    e.数组判等
        public static void main(String[] args) {
            int[] arr1 = {1, 2, 3};
            int[] arr2 = {1, 3, 2};
            System.out.println(Arrays.equals(arr1, arr2));
            System.out.println(Arrays.deepEquals(data1, data2));
        }
        -----------------------------------------------------------------------------------------------------
        Arrays.equals(Object[] array1, Object[] array2)
        判断两个一维数组是否相等
        数组元素为基本数据类型时,依次比较值
        数组元素为引用数据类型时,依次调用元素的 equals() 方法进行比较
        如果两个数组引用都为null,则认为它们是相等的
        如果两个数组元素值一样,但是两个数组对应位置元素不同,Arrays.equals返回结果是false
        -----------------------------------------------------------------------------------------------------
        Arrays.deepEquals(Object[] array1, Object[] array2)
        判断两个多维数组是否相等
        数组元素为基本数据类型时,依次比较值
        数组元素为引用数据类型时,依次调用元素的 equals() 方法进行比较
        如果两个数组引用都为null,则认为它们是相等的
        如果两个数组元素值一样,但是两个数组对应位置元素不同,Arrays.equals返回结果是false
    f.数组的哈希码
        public static void main(String[] args) {
            int[] arr1 = {1, 2, 3};
            System.out.println(Arrays.hashCode(arr1));
            System.out.println(Arrays.deepHashCode(data));
        }
        -----------------------------------------------------------------------------------------------------
        Arrays.hashCode(Object[] array)
        返回一维数组的哈希值
        -----------------------------------------------------------------------------------------------------
        Arrays.deepHashCode(Object[] array)
        返回多维数组的哈希值
    g.数组填充
        public static void main(String[] args) {
            int[] array = {12, 25, 3, 56, 89};
            Arrays.fill(array, 2);
            System.out.println(Arrays.toString(array));
        }
        -----------------------------------------------------------------------------------------------------
        Arrays.fill(Object[] array, Object obj)
        用指定元素填充整个数组 (会替换掉数组中原来的元素)
        -----------------------------------------------------------------------------------------------------
        Arrays.fill(Object[] array, int fromIndex, int toIndex, Object obj) 
        用指定元素填充数组,从起始位置到结束位置,取头不取尾 (会替换掉数组中原来的元素)
    h.数组的浅拷贝
        a.使用
            public static void main(String[] args) {
                int[] sourceArray = {1, 3, 5, 7, 0};
                int[] newArray = Arrays.copyOf(sourceArray, sourceArray.length);
                System.out.println(Arrays.toString(newArray));     // [1, 3, 5, 7, 0] 
            }
            -------------------------------------------------------------------------------------------------
            Arrays.copyOf(T[] original, int newLength)
            拷贝数组,其内部调用了System.arraycopy()方法,从下标0开始,如果超过原数组长度,则会用null进行填充
            -------------------------------------------------------------------------------------------------
            Arrays.copyOfRange(T[] original, int from, int to)
            拷贝数组,指定起始位置和结束位置,如果超过原数组长度,则会用null进行填充 
        b.证明浅拷贝
            class User {
                private String userNo;
            
                public User(String userNo){
                    this.userNo = userNo;
                }
            
                public String getUserNo() {
                    return userNo;
                }
            
                public void setUserNo(String userNo) {
                    this.userNo = userNo;
                }
            }
            public class DEMO {
                public static void main(String[] args) {
                    User[] sourceArray = {new User("N1"), new User("N2"),new User("N3")};
                    User[] newArray = Arrays.copyOf(sourceArray, sourceArray.length);
                    newArray[1].setUserNo("N4");
                    System.out.println(newArray[1].getUserNo());            // N4
                    System.out.println(sourceArray[1].getUserNo());         // N4
                }
            }
            -------------------------------------------------------------------------------------------------
            只是修改了新数组中的User的属性,结果原有数组的值也同样被修改了;
            说明数组的copy操作只是一个浅拷贝。这与序列化的浅拷贝完全相同:基本类型直接拷贝值,其他拷贝引用地址
        c.基于数组浅拷贝实现变长数组
            public static <T> T[] expandCapacity(T[] datas, int newLen) {
              // 校验长度值,如果小于0,则为0
              newLen = Math.max(newLen, 0);
              // 生成一个新数组,并拷贝原值,指定新的数组长度
              return Arrays.copyOf(datas, newLen);
            }
    i.数组的stream
        a.介绍
            static DoubleStream stream(double[] array) 
            返回顺序DoubleStream与指定的数组作为源  
            static IntStream stream(int[] array) 
            返回顺序IntStream与指定的数组作为源 
            static LongStream stream(long[] array) 
            返回顺序LongStream与指定的数组作为源
            static <T> Stream<T> stream(T[] array) 
            返回顺序Stream与指定的数组作为源  
            -------------------------------------------------------------------------------------------------
            static IntStream stream(int[] array, int startInclusive, int endExclusive) 
            返回顺序IntStream与指定的数组作为源的指定范围
            static DoubleStream stream(double[] array, int startInclusive, int endExclusive) 
            返回顺序DoubleStream与指定的数组作为源的指定范围
            static LongStream stream(long[] array, int startInclusive, int endExclusive) 
            返回顺序LongStream与指定的数组作为源的指定范围。 
            static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) 
            返回顺序Stream与指定的数组作为源的指定范围
            
            public static <T> Stream<T> stream(T[] array)
        b.Arrays.stream()将String数组转换为流
            public static void main(String[] args) {
                String[] arr = { "Geeks", "for", "Geeks" };
                Stream<String> stream = Arrays.stream(arr);
                stream.forEach(
                    str -> System.out.print(str + " ")
                );
            }
            -------------------------------------------------------------------------------------------------
            Geeks for Geeks 
        c.Arrays.stream()将int数组转换为流
            public static void main(String[] args) {
                int arr[] = { 1, 2, 3, 4, 5 };
                IntStream stream = Arrays.stream(arr);
                stream.forEach(
                    str -> System.out.print(str + " ")
                );
            }
            -------------------------------------------------------------------------------------------------
            1 2 3 4 5 
        d.Arrays.stream()将String数组转换为流
            String[] arr = {"Geeks", "for", "Geeks",
                "A", "Computer", "Portal"};
            Stream<String> stream = Arrays.stream(arr, 3, 5);
            stream.forEach(
                str -> System.out.print(str + " ")
            );
            -------------------------------------------------------------------------------------------------
            A Computer 
        e.Arrays.stream()将int数组转换为流
            public static void main(String[] args) {
                int arr[] = { 1, 2, 3, 4, 5 };
                IntStream stream = Arrays.stream(arr, 1, 3);
                stream.forEach(
                    str -> System.out.print(str + " ")
                );
            }
            -------------------------------------------------------------------------------------------------
            2 3 

06.java.util.Arrays,所有方法都是static静态方法
    a.Array、List、Set
        a.Array -> List
            public static void main(String[] args) {
                String[] ss = {"JJ", "KK", "MM"};
                List<String> list1 = Arrays.asList(ss);                         // 方式1:不可变List,不能add
                System.out.println(list1);
                list1.add("1");
                list1.add("2");
                list1.add("3");
                System.out.println(list1);    // 报错UnsupportedOperationException:不支持的操作异常

                List<String> list2 = new ArrayList<>(Arrays.asList(ss));        // 方式2:可变List
                System.out.println(list2);
                list2.add("1");
                list2.add("2");
                list2.add("3");
                System.out.println(list2);    // 不报错,编译通过

                List<String> list3 = Arrays.asList("AAA", "BBB", "CCC");        // 方式3:不可变List,不能add
                System.out.println(list3);
                list3.add("1");
                list3.add("2");
                list3.add("3");
                System.out.println(list3);    // 报错UnsupportedOperationException:不支持的操作异常
            }
            -------------------------------------------------------------------------------------------------
            public static <T> List<T> asList(T... a) {
                return new ArrayList<>(a);    // 默认java.util.Arrays的一个内部类ArrayList
            }
            调用Arrays.asList(),从asList可知,参数为可变参数,并且支持数组作为参数传入
            此时的list不能进行add操作,否则会报 “java.lang.UnsupportedOperationException”
            Arrays.asList()返回的是List,而且是一个定长的List,所以不能转换为ArrayList,只能转换为AbstractList
            原因在于asList()方法返回的是某个数组的列表形式,返回的列表只是数组的另一个视图,而数组本身并没有消失,
            对列表的任何操作最终都反映在数组上,所以不支持remove()、add()操作
            -------------------------------------------------------------------------------------------------
            Arrays$ArrayList:不支持增删操作;共享原始数据(Arrays$ArrayList元素与Arrays的数组元素是共享的)
            Arrays#asList返回的ArrayList只是Arrays一个内部类,并非真正的java.util.ArrayList
            java.util.ArrayList和Arrays$ArrayList都继承自AbstractList,而 java.util.Arrays$ArrayList并没有
            重写父类的add/remove方法,而父类方法恰恰都会抛出 UnsupportedOperationException
        b.List -> Array
            public static void main(String[] args) {
                List<String> list = new ArrayList<String>();
                list.add("AA");
                list.add("BB");
                list.add("CC");
                Object[] objects = list.toArray();                              // 方式1
                System.out.println(Arrays.toString(objects));

                String[] arr = new String[list.size()];                         // 方式2
                list.toArray(arr);                                              // 将 转换的数组 放入 arr 中
                System.out.println(Arrays.toString(arr));
            }
        c.Array -> Set
            public static void main(String[] args) {
                String[] arr = {"AA", "BB", "DD", "CC", "BB"};
                Set<String> set = new HashSet<String>(Arrays.asList(arr));      // 只有一种方式
                System.out.println(set);
            }
        d.Set -> Array
            public static void main(String[] args) {
                Set<String> set = new HashSet<String>();
                set.add("AA");
                set.add("BB");
                set.add("CC");

                String[] arr = new String[set.size()];                          // 只有一种方式
                set.toArray(arr);
                System.out.println(Arrays.toString(arr));
            }
        e.List -> Set
            public static void main(String[] args) {
                List<String> list = new ArrayList<String>();
                list.add("AA");
                list.add("BB");
                list.add("CC");

                Set<String> listSet = new HashSet<String>(list);                // 只有一种方式
                System.out.println(listSet);
            }
        f.Set -> List
            public static void main(String[] args) {
                Set<String> set = new HashSet<String>();
                set.add("AA");
                set.add("BB");
                set.add("CC");

                List<String> setList = new ArrayList<String>(set);              // 只有一种方式
                System.out.println(setList);
            }
    b.List、Set、Map
        a.Map -> List
            public static void main(String[] args) {
                Map<String, String> map = new HashMap<String, String>();
                map.put("A", "ABC");
                map.put("K", "KK");
                map.put("L", "LV");
                
                List<String> keyList = new ArrayList<String>(map.keySet());     // 将Map Key 转化为List
                System.out.println(keyList);

                List<String> valuesList = new ArrayList<String>(map.values());  // 将Map Value 转化为List
                System.out.println(valuesList);
            }
        b.Map -> Set
            public static void main(String[] args) {
                Map<String, String> map = new HashMap<String, String>();
                map.put("A", "ABC");
                map.put("K", "KK");
                map.put("L", "LV");

                Set<String> mapKeySet = map.keySet();                           // 将Map Key 转化为Set
                System.out.println(mapKeySet);

                // 将Map 的值转化为Set
                Set<String> mapValuesSet = new HashSet<String>(map.values());   // 将Map Value 转化为Set
                System.out.println(mapValuesSet);
            }

5.2 Optional

01.介绍
    a.Optional类
        Optional是个容器:它可以保存类型T的值,或者仅仅保存null。
        Optional类的引入很好的解决空指针异常,可以不用显式进行空值检测。
        Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
    b.类方法
        static <T> Optional<T> empty() 
        返回一个空的 Optional实例
        boolean equals(Object obj) 
        指示某个其他对象是否等于此可选项
        Optional<T> filter(Predicate<? super T> predicate) 
        如果一个值存在,并且该值给定的谓词相匹配时,返回一个 Optional描述的值,否则返回一个空的 Optional
        <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper) 
        如果一个值存在,应用提供的 Optional映射函数给它,返回该结果,否则返回一个空的 Optional
        T get() 
        如果 Optional中有一个值,返回值,否则抛出 NoSuchElementException
        int hashCode() 
        返回当前值的哈希码值(如果有的话),如果没有值,则返回0(零)
        void ifPresent(Consumer<? super T> consumer) 
        如果存在值,则使用该值调用指定的消费者,否则不执行任何操作 
        boolean isPresent() 
        返回 true如果存在值,否则为 false
        <U> Optional<U> map(Function<? super T,? extends U> mapper) 
        如果存在一个值,则应用提供的映射函数,如果结果不为空,则返回一个 Optional结果的 Optional
        static <T> Optional<T> of(T value) 
        返回具有 Optional的当前非空值的Optional
        static <T> Optional<T> ofNullable(T value) 
        返回一个 Optional指定值的Optional,如果非空,则返回一个空的 Optional
        T orElse(T other) 
        返回值如果存在,否则返回 other
        T orElseGet(Supplier<? extends T> other) 
        返回值(如果存在),否则调用 other并返回该调用的结果
        <X extends Throwable>
        T orElseThrow(Supplier<? extends X> exceptionSupplier) 
        返回包含的值(如果存在),否则抛出由提供的供应商创建的异常
        String toString() 
        返回此可选的非空字符串表示,适用于调试

02.使用
    a.创建对象
        a.静态方法ofNullable()
            Author author = getAuthor();
            Optional<Author> authorOptional = Optional.ofNullable(author);
            -------------------------------------------------------------------------------------------------
            在实际开发中,数据很多是从数据库获取的,Mybatis从3.5版本已经支持Optional,直接把dao方法的返回值类型
            定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回,封装的过程也不需要我们自己操作。
        b.静态方法of()
            Author author = new Author();
            Optional<Author> authorOptional = Optional.of(author);
            -------------------------------------------------------------------------------------------------
            一定要注意,如果使用of的时候传入的参数必须不为null。
            如果你确定一个对象不是空的则可以使用Optional的静态方法of来把数据封装成Optional对象。
        c.静态方法empty()
            Optional.empty()
            如果一个方法的返回值类型是Optional类型,而如果我们经判断发现某次计算得到的返回值为null,
            这个时候就需要把null封装成Optional对象返回。这时则可以使用Optional的静态方法empty来进行封装。
    b.安全消费值
        a.介绍
            获取到一个Optional对象后,可以使用其ifPresent方法对来消费其中的值。
            这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码,这样使用起来就更加安全了。
        b.代码
            Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
            authorOptional.ifPresent(author -> System.out.println(author.getName()));
    c.安全获取值
        a.介绍
            如果想获取值自己进行处理可以使用get方法获取,但是不推荐,因为当Optional内部的数据为空的时候会出现异常。
            如果我们期望安全的获取值,我们不推荐使用get方法,而是使用Optional提供的以下方法。
        b.orElseGet
            Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
            Author author1 = authorOptional.orElseGet(() -> new Author());
            -------------------------------------------------------------------------------------------------
            获取数据并且设置数据为空时的默认值。
            如果数据不为空就能获取到该数据,如果为空则根据你传入的参数来创建对象作为默认值返回。
        c.orElseThrow
            Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
            try {
                Author author = authorOptional.orElseThrow(
                                    (Supplier<Throwable>) () -> new RuntimeException("author为空")
                                );
                System.out.println(author.getName());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            -------------------------------------------------------------------------------------------------
            获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。
    d.过滤
        a.介绍
            使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。
        b.代码
            Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
            authorOptional.filter(author -> author.getAge()>100)
                          .ifPresent(author -> System.out.println(author.getName()));
    e.判断
        a.介绍
            使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。
            但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法。
        b.代码
            Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
            if (authorOptional.isPresent()) {
                System.out.println(authorOptional.get().getName());
            }
    f.数据转换
        a.介绍
            Optional提供map对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证使用安全。
        b.代码
            private static void testMap() {
                Optional<Author> authorOptional = getAuthorOptional();
                Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());
                optionalBooks.ifPresent(books -> System.out.println(books));
            }

5.3 stream.Stream

01.介绍
    a.JDK为什么引入Stream
        它与java.io包里的InputStream和OutputStream是完全不同的概念,
        Java8中的Stream是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作
        -----------------------------------------------------------------------------------------------------
        题目:求一个集合中字符串长度小于5的数量
        @Test
        public void lenIter() {
            List<String> list = Arrays.asList("java", "scala", "python", "shell", "ruby");
            int num = 0;
            for(String lan: list) {
                if(lan.length() < 5) {
                    num++;
                }
            }
            System.out.println(num);
        }
        -----------------------------------------------------------------------------------------------------
        题目:求一个集合中字符串长度小于5的数量
        @Test
        public void lenStream() {
            List<String> list = Arrays.asList("java", "scala", "python", "shell", "ruby");
            long num = list.parallelStream().filter(x -> x.length() < 5).count();
            System.out.println(num);
        }
    b.什么是Stream?执行一次、并行、高级版本的Stream
        Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。
        原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;
        高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、
        “获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
        -----------------------------------------------------------------------------------------------------
        Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了。
        而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。
        顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。
        而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。
        Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
        -----------------------------------------------------------------------------------------------------
        Stream将要处理集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
    c.Stream对流的操作
        Stream可以由数组或集合创建,对流的操作分为两种:
        中间操作,每次返回一个新的流,可以有多个。
        终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
    d.Stream和Collection的区别主要有哪些?
        stream本身并不存储数据,数据是存储在对应的collection里,或者在需要的时候才生成的;
        stream不会修改数据源,通常情况下会产生一个新的集合或一个值,总是返回新的stream;
        stream的操作是懒执行的:仅当最终的结果需要的时候才会执行,只有调用终端操作时,中间操作才会执行。

02.Builder内部接口
    a.介绍
        该方法将创建一个有序的Stream,其元素是添加到流生成器中的元素,按添加顺序排列。
    b.源码
        public interface Builder<T> extends Consumer<T> {
            @Override
            void accept(T t);

            default Builder<T> add(T t) {
                accept(t);
                return this;
            }

            Stream<T> build();
        }

03.java.util.stream.Steam类
    a.定义
        public interface Stream<T> extends BaseStream<T, Stream<T>> {
        -----------------------------------------------------------------------------------------------------
        继承BaseStream接口
    b.私有属性
        无
    c.公共属性
        无
    d.构造方法
        无
    d.非静态方法
        无
    e.静态方法
        具体见下一说明

04.java.util.stream.Steam类
    a.Stream的创建
        a.通过数组创建:java.util.Arrays.stream(T[] array)
            public static void main(String[] args) {
                int[] array={1,3,5,6,8};
                IntStream stream = Arrays.stream(array);
            }
        b.通过集合创建:java.util.Collection.stream()
            public static void main(String[] args) {
                List<String> list = Arrays.asList("a", "b", "c");
                Stream<String> stream = list.stream();                     // 创建一个顺序流
                Stream<String> parallelStream = list.parallelStream();     // 创建一个并行流
            }
        c.通过Stream的静态方法:of()、iterate()、generate()
            public static void main(String[] args) {
                Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);                // of()

                Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);  // iterate()
                stream2.forEach(System.out::println);

                Stream<Double> stream3 = Stream.generate(Math::random).limit(3);     // generate()
                stream3.forEach(System.out::println);
            }
        d.使用BufferedReader.lines()方法:将每行内容转成流
            BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
            Stream<String> lineStream = reader.lines();
            lineStream.forEach(System.out::println);
        e.使用Pattern.splitAsStream()方法:将字符串分隔成流
            Pattern pattern = Pattern.compile(",");
            Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
            stringStream.forEach(System.out::println);
    b.Stream的转换
        a.filter操作:原stream中满足条件的元素构成新的stream
            public static void main(String[] args) {
                List<String> list = Arrays.asList("java", "scala", "python", "shell", "ruby");
                long num = list.parallelStream().filter(x -> x.length() < 5).count();
                System.out.println(num);
            }
            -------------------------------------------------------------------------------------------------
            2
        b.map操作:遍历原stream中的元素,转换后构成新的stream
            public static void main(String[] args) {
                List<String> list = Arrays.asList(new String[]{"a", "b", "c"});
                List<String> result = list.stream().map(String::toUpperCase).collect(Collectors.toList());
                result.forEach(x -> System.out.print(x + " "));
            }
            -------------------------------------------------------------------------------------------------
            A B C
        c.distinct操作
            public static void main(String[] args) {
                Stream<String> distinctStream = Stream.of("bj","shanghai","tianjin","bj","shanghai").distinct();
                Stream<String> sortedStream = distinctStream.sorted(Comparator.comparing(String::length));
                sortedStream.forEach(x -> System.out.print(x + " "));
            }
            -------------------------------------------------------------------------------------------------
            bj tianjin shanghai 
        d.排序操作
            public static void main(String[] args) {
                Stream<Integer> sortedStream = Stream.of(1,3,7,4,5,8,6,2).sorted();
                sortedStream.collect(Collectors.toList()).forEach(x -> System.out.print(x + " "));
                System.out.println();

                Stream.of(1,3,7,4,5,8,6,2).sorted(new Comparator<Integer>() {
                    @Override
                    public int compare(Integer o1, Integer o2) {
                        return o1 - o2;
                    }
                });
                Stream<Integer> sortedReverseStreamV2 = Stream.of(1,3,7,4,5,8,6,2)
                                	.sorted((Integer o1, Integer o2) -> o2 - o1);
                sortedReverseStreamV2.collect(Collectors.toList()).forEach(x -> System.out.print(x + " "));
            }
            -------------------------------------------------------------------------------------------------
            1 2 3 4 5 6 7 8 
            8 7 6 5 4 3 2 1 
    c.reduction操作
        a.reduction操作
            reduction就是从stream中取出结果,是terminal operation,因此经过reduction后的stream不能再使用了。
            主要包含以下操作: findFirst()/findAny()/allMatch/anyMatch()/noneMatch等等
            -------------------------------------------------------------------------------------------------
            public static void main(String[] args) {
                Stream<String> wordList = Stream.of("bj", "tj", "sh", "yy", "yq").distinct();
                Optional<String> firstWord = wordList.filter(word -> word.startsWith("y")).findFirst();
                System.out.println(firstWord.orElse("unknown"));
            }
            -------------------------------------------------------------------------------------------------
            yy
        b.reduce方法
            public static void main(String[] args) {
                Stream<Integer> list = Stream.of(1, 2, 3, 4, 5);
                Optional<Integer> result = list.reduce((x, y) -> x + y);
                System.out.println(result);
            }
            -------------------------------------------------------------------------------------------------
            Optional[15]
    d.collect操作
        a.介绍
            collect()方法可以对stream中的元素进行各种处理后,得到stream中元素的值。
            并且Collectors接口提供了很方便的创建Collector对象的工厂方法。
        b.代码
            public static void main(String[] args) {
                List<String> list = Stream.of("hello", "world", "hello", "java")
                                	.collect(Collectors.toList());
                list.forEach(x -> System.out.print(x + " "));
                System.out.println();

                Set<String> set = Stream.of("hello", "world", "hello", "java")
                                	.collect(Collectors.toSet());
                set.forEach(x -> System.out.print(x + " "));
                System.out.println();

                Set<String> treeSet = Stream.of("hello", "world", "hello", "java")
                                	.collect(Collectors.toCollection(TreeSet::new));
                treeSet.forEach(x -> System.out.print(x + " "));
                System.out.println();

                String resultStr = Stream.of("hello", "world", "hello", "java")
                                	.collect(Collectors.joining(","));
                System.out.println(resultStr);
            }
            -------------------------------------------------------------------------------------------------
            hello world hello java 
            world java hello 
            hello java world 
            hello,world,hello,java

6 org.apache.commons.lang3

6.1 ArrayUtils

01.org.apache.commons.lang3.ArrayUtils,所有方法都是static静态方法
    a.数组1 + 数组2 -> 合并为数组3
        public static void main(String[] args) {
            int[] array1 = { 1, 2, 3, 4, 5 };
            int[] array2 = { 6, 7, 8, 9, 10 };
            int[] array3 = org.apache.commons.lang3.ArrayUtils.addAll(array1, array2);
            System.out.println(Arrays.toString(array3));
        }
        -----------------------------------------------------------------------------------------------------
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    b.String数组 -> 字符串(使用指定字符拼接)
        public static void main(String[] args) {
            String[] array = { "a", "b", "c" };
            String str = org.apache.commons.lang3.StringUtils.join(array, ", ");
            System.out.println(str);
        }
        -----------------------------------------------------------------------------------------------------
        a, b, c
    c.数组逆序
        public static void main(String[] args) {
            int[] array = { 1, 2, 3, 4, 5 };
            org.apache.commons.lang3.ArrayUtils.reverse(array);
            System.out.println(Arrays.toString(array));
        }
        -----------------------------------------------------------------------------------------------------
        [5, 4, 3, 2, 1]
    d.数组元素移除
        public static void main(String[] args) {
            int[] array = { 1, 2, 3, 4, 5 };
            int[] removedArray = org.apache.commons.lang3.ArrayUtils.removeElement(array, 3);
            System.out.println(Arrays.toString(removedArray));
        }
        -----------------------------------------------------------------------------------------------------
        [1, 2, 4, 5]

02.org.apache.commons.lang3.ArrayUtils,所有方法都是static静态方法
    a.数组转Map
        String[][] countries = {
            {"United States", "New York"}, {"United Kingdom", "London"},
            {"Netherland", "Amsterdam"}, {"Japan", "Tokyo"}, {"France", "Paris"}
        };
        Map countryCapitals = ArrayUtils.toMap(countries);
        System.out.println("Capital of Japan is " + countryCapitals.get("Japan"));
        System.out.println("Capital of France is " + countryCapitals.get("France"));
        -----------------------------------------------------------------------------------------------------
        public static Map<Object, Object> toMap(final Object[] array) {
            if (array == null) {
                return null;
            }
            final Map<Object, Object> map = new HashMap<>((int) (array.length * 1.5));
            for (int i = 0; i < array.length; i++) {
                final Object object = array[i];
                if (object instanceof Map.Entry<?, ?>) {
                    final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
                    map.put(entry.getKey(), entry.getValue());
                } else if (object instanceof Object[]) {
                    final Object[] entry = (Object[]) object;
                    if (entry.length < 2) {
                        throw new IllegalArgumentException("Array element " + i + ", '"
                            + object
                            + "', has a length less than 2");
                    }
                    map.put(entry[0], entry[1]);
                } else {
                    throw new IllegalArgumentException("Array element " + i + ", '"
                            + object
                            + "', is neither of type Map.Entry nor an Array");
                }
            }
            return map;
        }
        map.put(entry.getKey(), entry.getValue()); 从toMap可知,默认会把Map左边当作key,右边当作value

6.2 StringUtils