1 变量、常量、运算符

1.1 变量

01.图示
                 【首字母】                       【其余部分】
                   字母                              字母
    变量名   =     下划线_                           下划线_
                   美元$                             美元$
                                                     数字
               变量第一字符只能:字母、_、$    变量只能使用:字母、_、$、数字
               变量第一字符不能:数字
               中文也可命名变量,但不建议

02.见名知意
    包名          全部小写                 域名倒置
    类名          首字母大写+驼峰原则       Man,GoodMan
    方法名        首字母小写+驼峰原则       run(), runRun()
    全局变量      首字母小写+驼峰原则       monthSalary
    局部变量      首字母小写+驼峰原则       monthSalary
    常量          全部字母大写+下划线       MAX_VALUE

03.变量含义标识符:名词性词组
    变量名             目标词 + 动词(的过去分词) + [状语] + [目的地]
    DataGotFromSD       Data        Got            Form     SD          从SD中获取数据
    DataDeleteFromSD    Data        Deleted        Form     SD          从SD中删除数据

04.函数含义标识符:动词性词组
    变量名             动词(一般现在时)+ 目标词 + [状语] + [目的地]
    GetDataFromSD       Get               Data      Form    SD          从SD中获取数据
    DeleteDataFromSD    Delete            Data      Form    SD          从SD中删除数据

05.优先级
    括号          ()  []  {}
    自增          a++ a-- ++a --a
    按位取反      ~
    逻辑非        !
    ---------------------------------------------------------------------------------------------------------
    算数运算符    *  /  %  +  -
    ---------------------------------------------------------------------------------------------------------
    位运算符      >> >>> <<
    关系运算符    >  <   >=  <=  ==  !=
    ---------------------------------------------------------------------------------------------------------
    位运算符      &  ^  |
    ---------------------------------------------------------------------------------------------------------
    逻辑运算符    && ||
    ---------------------------------------------------------------------------------------------------------
    条件运算符    ? : ;
    赋值运算符    =  +=  -=  *=  /=  %=  <<=  >>=  &=  |=  ^=
    ---------------------------------------------------------------------------------------------------------
    instanceof   保留关键字,二元操作符,类似==、>、<),例如,if ("sss" instanceof String) { ... }
    instanceof   判断【其左边对象是否为其右边类的实例】,也可以用来判断【继承中的子类的实例是否为父类的实现】
    instanceof   bc.equals(c) 需比较 c instanceof BigCar

06.关键字(共46个)
    数据类型     【byte、short、int、long、float、double、char、boolean】
    数据类型     【new、void、instanceof】
    ---------------------------------------------------------------------------------------------------------
    语句         【if、else、return】、【switch、case、default】、【continue、break】、【for、do、while】
    语句         【try、catch、throws、finally】、【throw、this、super】
    ---------------------------------------------------------------------------------------------------------
    修饰         【public、private、protected、abstract】【final、static、native、transient】
    修饰         【synchronized、volatile】
    ---------------------------------------------------------------------------------------------------------
    包           【package、import】
    类/接口      【class、interface】、【extends、implements】
    ---------------------------------------------------------------------------------------------------------
    null         【既不是对象也不是一种类型,一种特殊的值,可以将其赋予任何引用类型,也可以将null转化成任何类型】
    const        【JAVA目前没有const,但const是保留字,类似final,为后续JDK版本的扩展,而进行保留】
    goto         【JAVA目前没有goto,但goto是保留字,在C++中,break跳不出多重循环,只能使用goto指定要跳出的地点】
    ---------------------------------------------------------------------------------------------------------
    JAVA无意义   【future、generic、operator、outer、rest、var】

1.2 常量

01.常量值(也称字面量)
    a.数字
                       整数                                                小数
        十进制数            由数字和小数点组成,且必须有小数点         单精度     float类型后面一定要加f(F)
        八进制整型常量      以0开头                                  双精度     默认double
        十六进制整型常量    以0x或0X开头
        长整型              以L作结尾
    b.非数字
        字符型              字符常量(两个单引号)、字符串常量(两个双引号)
        布尔型              只有两个值,false(假)和 true(真)

02.定义常量,主要是用来避免魔法数字和提高代码可读性
    a.四种实现方式
        接口常量                                             接口变量默认public static final
        类常量                                               类常量显示声明public static final
        枚举
        文件,如Properties文件
    b.实例
        public interface ConstantInterface {
            String SUNDAY = "SUNDAY";                        接口变量默认public static final
        }
        -----------------------------------------------------------------------------------------------------
        public class ConstantClass {
            public static final int SUNDAY = 0;              类常量显示声明public static final
        }
        public final class ConstantClass {
            public static final int SUNDAY = 0;              类使用 final class 定义
        }
        public class ConstantClass {
            private static final int SUNDAY = 0;
            public static String getSunday() {               类使用 get方法 获取
                return SUNDAY;  
            }
        }
        -----------------------------------------------------------------------------------------------------
        enum Color {
            RED, GREEN, BLUE;
        }
        enum Size {
            SMALL("S"),
            MEDIUM("M"),

            private String suoxie;
            private Size(String suoxie){
                this.suoxie = suoxie;
            }
            public String getSuoxie(){
                return suoxie;
            }
        }
        enum Dict {
            PROSTA("PROSTA","产品状态"),
            COUNTRY("COUNTRY","国家");

            private final String value;
            private final String name;
            Dict(String value, String name){
                this.value=value;
                this.name=name;
            }
            public String getValue() {
                return value;
            }
            public String getName() {
                return name;
            }
        }
        public class Test {
            public static void main(String[] args) {
                System.out.println(Color.BLUE);
                System.out.println(Size.MEDIUM.getSuoxie());
                System.out.println(Dict.PROSTA.getName());
                System.out.println(Dict.PROSTA.getValue());
            }
        }

03.正则表达式 = 转义字符 + 元字符,ASCII,转义字符串,【JAVA默认Unicode编码,兼容ASCII编码】
    a.转义字符
        转义字符             意义                                 ASCII码值(十进制)
        \a                  响铃(BEL)                                007
        \b                  退格(BS) ,将当前位置移到前一列            008
        \f                  换页(FF),将当前位置移到下页开头           012
        \n                  换行(LF) ,将当前位置移到下一行开头        010
        \r                  回车(CR) ,将当前位置移到本行开头          013
        \t                  水平制表(HT) (跳到下一个TAB位置)         009
        \v                  垂直制表(VT)                             011
        \\                  代表一个反斜线字符''\'                    092
        \'                  代表一个单引号(撇号)字符                 039
        \"                  代表一个双引号字符                        034
        \?                  代表一个问号                              063
        \0                  空字符(NUL)                              000
        \ddd                1到3位八进制数所代表的任意字符             三位八进制
        \xhh                十六进制所代表的任意字符                  十六进制
    b.元字符
        ^                                               --断言目标的开始位置(或多行模式下的行首位置)
        $                                               --断言目标的介绍位置(或多行模式下的结尾位置)
        .                                               --匹配除换行符外的其他任何字符
        [                                               --匹配字符类定义
        ]                                               --介绍内字符定义
        |                                               --开始一个可选分支
        (                                               --子组的开始标记
        )                                               --子组的结束标记
        ?                                               --量词,表示0次或多次
        *                                               --量词,0次或多次匹配
        +                                               --量词,1次或多次匹配
        {                                               --自定义量词的开始标记
        }                                               --自定义量词的结束标记
        -----------------------------------------------------------------------------------------------------
        \d                                              --数字
        \D                                              --非数字
        \s                                              --空白字符
        \S                                              --非空白字符
        \w                                              --单词字符
        \W                                              --非单词字符
    c.ASCII
        a-z 十进制码是97-122,八进制码是141-172,"\141" === "a" && "\141" === "\a" ->true
        A-Z 十进制码是65-90,八进制码是101-132, "\101" === "A" && "\101" === "\A" -> true
        0-9 十进制是48-57,八进制码是060-071,"\060" === "0" && "\060" === "\0" ->true
        -----------------------------------------------------------------------------------------------------
        ASCII转义字符与Regex转义字符,二者的转义字符是同一样东西,都代表特殊意义的字符;
        ASCII可以描述一些无法显示的特殊字符,因此,可以应用于任何地方;
        ASCII中的转义字符可以用于【正则匹配】,甚至可以将【正则匹配中的转义字符、元字符,变为对应的ASCII用于正则】
    d.转义字符串(Escape String),即字符实体(Character Entity)
        第一部分:一个&符号,英文叫ampersand;第二部分:实体名字,或者#加上实体编号;第三部分:一个;分号
        -----------------------------------------------------------------------------------------------------
        html转义符、java转义符、xml转义符、 oracle转义符、sql转义符

1.3 运算符

01.算数运算符(6个)(JAVA中仅有的两个重载过的运算符:String类中的"+"、"+=")
    ++      自增     ++a,--a  前缀  先自增/自减,再使用
    --      自减     a++, a--  后缀  先使用,再自增/自减
    ---------------------------------------------------------------------------------------------------------
    *       数字"*"
    /       数字"/"
    %       数字"%":求余取模(有符号问题,结果永远和"被除数一致":10%-3=1,-10%-3=-1,10%3=1,-10%3=1)
    ---------------------------------------------------------------------------------------------------------
    +       数字"+"、字符串拼接"+"      坑:num += 10; 不同于 num =+ 10;(等价于num=10,+10代表"正数+10")
    -       数字"-"

02.关系运算符
    >
    <
    >=
    <=
    ---------------------------------------------------------------------------------------------------------
    ==
    !=

03.位运算符:仅对byte,short,char —> int —> long有效,不涉及float —> double
    ~       按位取反    翻转操作数的每一位,即0变成1,1变成0
    ---------------------------------------------------------------------------------------------------------
    <<      按位左移    左操作数按位左移右操作数指定的位数
    >>>     按位右移补零 左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充
    >>      按位右移    左操作数按位右移右操作数指定的位数
    ---------------------------------------------------------------------------------------------------------
    &       按位与      如果相对应位都是1,则结果为1,否则为0
    ^       按位异或    如果相对应位值相同,则结果为0,否则为1
    |       按位或      如果相对应位都是0,则结果为 0,否则为 1
    ---------------------------------------------------------------------------------------------------------
    一个符号:不根据真值表进行判断(两边都会判断),位运算符
    Console.log(2<1 &  1/0 == 0);                                   |   报错,Exception in :by zero
    Console.log(2<1 |  1/1 == 0);                                   |   false

04.逻辑运算符
    !       逻辑非     用来反转操作数的逻辑状态,如果条件为true,则逻辑非运算符将得到false
    ---------------------------------------------------------------------------------------------------------
    &&      逻辑与     当且仅当两个操作数都为真,条件才为真
    ||      逻辑或     如果任何两个操作数任何一个为真,条件为真
    ---------------------------------------------------------------------------------------------------------
    两个符号:根据真值表进行判断(短路特性),逻辑运算符
    Console.log(2<1 && 1/0 == 0);                                   |   false && ... = false
    Console.log(2>1 || 1/0 == 0);                                   |   true  || ... = true

05.条件运算符(三目运算符)
    X       x         = 布尔值判断?     为真:   为假;
    boolean myBoolean = (myInt == 0) ? false : true;

06.赋值运算符
    =
    ---------------------------------------------------------------------------------------------------------
    +=
    -=
    *=
    /=
    %=
    ---------------------------------------------------------------------------------------------------------
    <<=
    >>=
    &=
    |=
    ^=

1.4 null使用

01.介绍
    a.历史
        Java的设计原理是为了简化事情,没有浪费时间在指针、操作符重载、多继承实现的原因,但null却与此正好相反;
    b.为什么需要学习null?
        因为如果你对null不注意,Java将使你遭受空指针异常的痛苦,并且得到一个沉痛的教训。
    c.JAVA中NULL是什么?
        null是JAVA中一个很重要的概念。
        null设计初衷是为了表示一些缺失的东西,例如缺失的用户、资源或其他东西。
        但是,一年后,令人头疼的空指针异常给Java程序员带来不少的骚扰。
    d.总结
        null是任何一个引用类型变量的默认值
        null不能调用任何“非静态方法”,会报错
        null可以通过instanceof判断类型

02.深入了解
    a.NULL大小写
        null是Java中的关键字,像public、static、final一样,对大小写敏感;
        不能将null写成Null或NULL,会导致编译器无法识别而报错
    b.NULL特殊值
        null既不是对象也不是一种类型,它仅是一种特殊的值,你可以将其赋予任何引用类型,也可以将null转化成任何类型
        String str = null;
        Integer itr = null;
        Double dbl = null;
        String myStr = (String) null;
        Integer myItr = (Integer) null;
        Double myDbl = (Double) null;
        System.out.println(myStr);          // null
        System.out.println(myItr);          // null
        System.out.println(myDbl);          // null
        不难发现,在编译和运行时期,将null强制转换成任何引用类型都是可行的,而且在运行时期都不会抛出空指针异常
    c.null可以赋值给引用变量,但不能赋值给基本变量
        int i = null;                       // 提示报错
        short s = null;                     // 提示报错
        byte b = null;                      // 提示报错
        double d = null;                    // 提示报错
        Integer itr = null;                 // 提示不报错
        int j = itr;                        // 提示不报错,但是运行时空指针报错
        不难发现,将null赋值给包装类object,然后将object赋给各自的基本类型,编译器不会报错,但是运行时期报错;
        报错内容为:Exception in thread "main" java.lang.NullPointerException,这是因为Java中的自动拆箱导致的
    d.null包装类
        任何含有null值的包装类在Java拆箱生成基本数据类型时候都会抛出一个空指针异常。一些程序员犯这样的错误,
        他们认为自动装箱会将null转换成各自基本类型的默认值,
        ----------------------------------------------------------
        例如对于int转换成0,布尔类型转换成false,但是那是不正确的,如下面所示:
        Integer iAmNull = null;
        int i = iAmNull;                    // 空指针报错
        不能发现,在使用HashMap和Integer键值的时候会发生很多这样的错误
        ----------------------------------------------------------
        Map numberAndCount = new HashMap<>();
        int[] numbers = {3, 5, 7,9, 11, 13, 17, 19, 2, 3, 5, 33, 12, 5};
        for(int i : numbers){
            int count = numberAndCount.get(i);
            numberAndCount.put(i, count++); // 空指针报错
        }
        ----------------------------------------------------------
        场景:找到一个数字在数组中出现了多少次
        首先得到以前的数值,然后再加一,最后把值放回Map里。
        程序员可能会以为,调用put方法时,自动装箱会自己处理好将int装箱成Interger,
        但是他忘记了当一个数字没有计数值的时候,HashMap的get()方法将会返回null,而不是0
        Integer的默认值是null,当把null值传递给一个int型变量的时候自动装箱将会返回空指针异常。
    e.null使用instanceof判断类型
        Integer iAmNull = null;
        if(iAmNull instanceof Integer){
            System.out.println("iAmNull is instance of Integer"); 
        }else{
            System.out.println("iAmNull is NOT an instance of Integer");
        }
        这是instanceof操作一个很重要的特性,使得对类型强制转换检查很有用
    f.非静态方法调用null的引用类型变量
        public class DEMO {
            public static void main(String[] args) {
                DEMO myObject = null;
                myObject.iAmStaticMethod();             // 不抛异常
                myObject.iAmNonStaticMethod();          // 空指针异常
            }
            private static void iAmStaticMethod(){
                System.out.println("静态方法使用静态绑定,不会抛出空指针异常");
            }
            private void iAmNonStaticMethod(){
                System.out.println("非静态方法,抛出指针异常");
            }
        }
        用静态方法来使用一个值为null的引用类型变量,因为静态方法使用静态绑定,不会抛出空指针异常
    g.null传值
        public void print(Object obj) 可以这样调用 print(null),
        null传递给方法使用,方法可以接收任何引用类型,不会抛出空指针异常,只是优雅的退出
    h.null比较
        String abc = null;
        String cde = null;
        if (abc == cde) {
            System.out.println("null == null is true in Java");
        }
        if (null != null) {
            System.out.println("null != null is false in Java");
        }
        ==或者!=操作来比较null值,但不能使用小于或者大于;跟SQL不同,在Java中null==null将返回true

2 数据类型、类型转换

2.1 数据类型

01.介绍
    类型                  默认值                     位数                            范围
    byte                    0                       8 bits                     -128 to 127
    short                   0                      16 bits                   -32768 to 32767
    int                     0                      32 bits              -2147483648 to 2147483647
    float                  0.0f                    32 bits                 ±1.4E-45 to ±3.4028235E+38
    long                    OL                     64 bits     -9223372036854775808 to 9223372036854775807
    double                 0.0d                    64 bits                ±4.9E-324 to ±1.7976931348623157E+308
    boolear               FALSE                     1 bit                           NA
    char                 'u0000'                   16 bits                   \u000o to \uFFFF
    ---------------------------------------------------------------------------------------------------------
    计算机程序中的“小数”为什么称为“浮点数”?
        计算机中的数字,可以分为“定点数”和“浮点数”
        定点数是指:小数点固定在某个位置上的数据
        浮点数是指:小数点位置可以浮动的数据,例如3141.59*10^3和3.14159*10^6表示同一个数字,但原数中小数点的位置不同
        显然,浮点数更加灵活、可表示的范围更大。
    ---------------------------------------------------------------------------------------------------------
    Java中,3*0.3f的运算结果是:0.90000004;
        因为float是32位的,最多能够容纳下2的32次方个数字
        但f1oat小数,包含着无穷无尽数字,因此2的32次方个位置无法容纳全部小数,只能近似,既然是近似,就会出现一些误差

02.原生数据类型(基础数据类型)(内置数据类型)
    a.数字
                整数范围,默认int              精度范围永远小于         小数范围,默认double
        字节    byte  = 8bits  = 1字节  -2^7~2^7-1        单精度  float  = 32bits = 4字节 -2^149~2^128-1
        短整型  short = 16bits = 2字节  -2^15~2^15-1      双精度  double = 64bits = 8字节 -2^1074~2^1024-1
        整形    int   = 32bits = 4字节  -2^31~2^31-1
        长整型  long  = 64bits = 8字节  -2^63~2^63-1
    b.非数字
        字符型   char = 16bit  = 2字节  \u0000~\uFFFF
        布尔型   boolean = 1bit/未定    true、false
    ---------------------------------------------------------------------------------------------------------
    a.数字
                整数范围,默认int              精度范围永远小于            小数范围,默认double
        字节    byte  = 8bits  = 1字节  默认0                 单精度  float  = 32bits = 4字节   默认0.0f
        短整型  short = 16bits = 2字节  默认0                 双精度  double = 64bits = 8字节   默认0.0d
        整形    int   = 32bits = 4字节  默认0
        长整型  long  = 64bits = 8字节  默认0L
    b.非数字
        字符型   char = 16bit  = 2字节  默认'\u0000'   单引号,单字符(1个汉字,2字节)   -2^15 ~ 2^15-1
        布尔型   boolean = 1bit/未定    默认False(0 false,1 true)
    ---------------------------------------------------------------------------------------------------------
    a.数字
                整数范围,默认int              精度范围永远小于            小数范围,默认double
        字节    byte  = 8bits  = 1字节  Byte     -128~127     单精度  float  = 32bits = 4字节   Float
        短整型  short = 16bits = 2字节  Short    -128~127     双精度  double = 64bits = 8字节   Double
        整形    int   = 32bits = 4字节  Integer  -128~127
        长整型  long  = 64bits = 8字节  Long     -128~127
    b.非数字
        字符型   char = 16bit  = 2字节  Character
        布尔型   boolean = 1bit/未定    Boolean  (0 false,1 true)
    
03.引用数据类型(对象类型)
    类/对象,String = 英文字符占1个、中文字符占2个(GBK)/3个(UTF-8)字节  默认null  双引号,任意字符
            String不属于基本数据类型,只是代表一个类,属于引用类型,【对象默认null,所以String默认值也是null】
            String也可不用new的形式来创建对象呢?【Java字符串常量池,不需要用new关键字,也会在常量池中创建对象】
    接口(interface)
    枚举(enum)
    注解(annotation)
    数组(Array)

04.二者区别
    a.概念
        原生数据类型:【内置】
        引用数据类型:【通过new关键字,将创建对象保存在堆中,然后在栈中创建引用,并指向该对象在堆中的地址】
        原生数据类型:【变量名指向“具体数值”】
        引用数据类型:【变量名指向存储数据对象的“内存地址”,即“堆中地址”】
    b.内存
        原生数据类型:【声明后,JVM会立即分配内存空间给它】
        引用数据类型:【声明后,JVM不会分配内存空间给它,只是以“引用方式”指向"具体数值"】
    c.默认值
        原生数据类型:【默认:对应数据类型的默认值】
        引用数据类型:【只声明,不NEW实例化:对象默认“NULL”,且为全局变量】
                     【对象默认“非NULL”,且“对象内部的属性值,都是对应数据类型的默认值”】
    d.判断问题
        ==:关系运算符,对象内存地址,【原生数据类型(地址中储存数值),引用数据类型(地址中存储指向堆中的地址)】
        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方法
            }
        }
    e.比较1
        String name = "zs";
        String pwd = "123";
        if(name.equals("zs") && pwd.equals("123") {
            System.out.println("成功!")
        }
        运算符“==”不能应用于“java.lang.String”、“int”,应当使用"equals"
    f.比较2
        String类,重写了Object类中的equals、hashCode方法,并且在Object类上,进行了各自的扩展;
        例如,equals方法不仅仅是对【先对象的引用地址进行判断,再对"字符"进行逐个比对判断】,源码如下:
        -------------------------------------------------------------------------------------------------
        public boolean equals(Object obj) {                     // Object类
            return (this == obj);
        }
        -------------------------------------------------------------------------------------------------
        public boolean equals(Object anObject) {                // String类
            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;
        }

05.boolean转换(0 false,1 true)【由于boolean只能存放true或false,不兼容整数或字符,因此不参与“自动类型转换”】
    a.对应关系
        boolean转化为int       0 false       1 true    JAVA本身不支持直接强转,boolean与Int无法转换
        int转化为boolean       0 false     非0 true
    b.boolean转化为int:唯一方法(三目运算符)
        boolean myBoolean = true;
        int myInt = myBoolean ? 1 : 0;
        System.out.println("boolean true 对应 int " + myInt);             |  boolean true 对应 int 1
        -------------------------------------------------------------------------------------------------
        boolean myBoolean = false;
        int myInt = myBoolean ? 1 : 0;
        System.out.println("boolean false 对应 int " + myInt);            |  boolean false 对应 int 0
    c.int转化为boolean
        int myInt = 2;
        boolean myBoolean = myInt!= 0;
        System.out.println("int " + myInt + " 对应 boolean " + myBoolean);|  int 2 对应 boolean true
        -------------------------------------------------------------------------------------------------
        int myInt = 0;
        boolean myBoolean = myInt != 0;
        System.out.println("int " + myInt + " 对应 boolean " + myBoolean);|  int 0 对应 boolean false
        -------------------------------------------------------------------------------------------------
        int myInt = 2;
        boolean myBoolean = (myInt == 0) ? false : true;
        System.out.println("int " + myInt + " 对应 boolean " + myBoolean);|  int 0 对应 boolean true
        -------------------------------------------------------------------------------------------------
        int myInt = 0;
        boolean myBoolean = (myInt == 0) ? false : true;
        System.out.println("int " + myInt + " 对应 boolean " + myBoolean);|  int 0 对应 boolean false
    d.为什么Java布尔值只接受true或false,而不接受1或0?
        与C和C++之类的语言不同,Java将boolean视为完全独立的数据类型,它具有2个不同的值:true和false
        与C和C++之类的语言不同,Java将值1和0的类型为int的数据类型,而int不能隐式转换为boolean
        在JVM层面,boolean、byte、short、char、int当作int处理,而boolean为0和1,因此“不可隐式转换”仅在Java层面

2.2 类型转换

01.基础类型的转换
    a.自动类型转换
        a.范围小 -> 范围大
            byte,short,char —> int —> long —> float —> double
            【byte,short,char同一等级,彼此不会进行转换】
            【由于boolean只能存放true或false,不兼容整数或字符,因此不参与“自动类型转换”】
        b.转换时的混合运算
            运算中,【不同类型的数据先转化为同一类型】,然后进行运算,如,int + long + float,运算时,类型依次转换
            整数类型与小数类型做运算,【类型会变为小数类型】;
            -------------------------------------------------------------------------------------------------
            范围小的类型【自动转换/自动赋值】给范围大的类型;
            范围小的类型【遇到】范围大的类型,【自动转换】为范围大的类型;
            范围最大的是【字符串String】:任何类型遇到String,都会自动变为字符串
        c.举例
            int a   = 'A'+ 1234;    正确,char遇到int,会自动转换为int
            int b   = 10 + 3.14;    报错,Required type: int  Provided: double
        d.误区
            int c   = (x + y + z)/3 输出:98     int + int + int/3 = int           实际98.333 -> 98
            double c= (x + y + z)/3 输出:98.0   int + int + int/3 = int -> double 实际98.333 -> 98 -> 98.0
    b.强制类型转换:必须强转,精度损失
        a.范围大 -> 范围小
            范围小 = (小类型)范围大;
        b.举例
            float a = 1234.5f;      正确
            int   a = 1234.5d;      报错,Required type: int   Provided: double
            float a = 1234.5d;      报错,Required type: float Provided: double
    c.(隐含)强制类型转换
        a.float/double,没有内置转换器
            float f = 123.4;        小数,默认类型double,报错,Required type: float  Provided: double
                                    推荐:【float f = 1234.f;】【float f = (float)123.4;】
        b.int,内置转换器:=、+=
            short a = 1000;         整数,默认类型int,【但内置转换器】,short范围小 = int范围大,正确
            byte  b = 10;           整数,默认类型int,【但内置转换器】,byte范围小  = int范围大,正确
            long  c = 123;          整数,默认类型int,【自动类型转换】,long范围大  = int范围小,正确
        c.byte,short特殊情况,整数中只有=,+=内置转换器
            byte b1 = 100;
            byte b2 = 100;
            b2 = b1 + b2;           报错,Required type: byte  Provided: int
            -------------------------------------------------------------------------------------------------
            byte b1 = 100;
            byte b2 = 100;
            b2 += b1;               正确
            -------------------------------------------------------------------------------------------------
            结论:byte + byte = int,short + short = int,(byte范围:-128 ~ 127)
        d.左右两侧的类型必须一致,但是【数据值只需要兼容,类型本身或范围比其小的类型】
            long[] longs = new long[]{(int) 33, 2, 1, 2, 455};   正确
            long[] longs = new long[]{(float) 33, 2, 1, 2, 455}; 报错,Required type: long Provided: float

02.包装类的转换:从JDK1.5开始,JVM(Java Virtual Machine)实现【基本类型与对应包装类的自动转换】
    a.介绍
        Everything is an object,万物皆对象,都有自己的属性和方法;
        通过自动装箱、自动拆箱功能,可以大大简化基本类型变量和包装类对象之间的转换过程;
        基本类型(无属性、无方法,存储相对简单、运算效率较高)-> 引用类型(包装类,有属性、有方法,满足一切皆对象)
    b.包装类
        a.数字
                    整数范围,默认int              精度范围永远小于            小数范围,默认double
            字节    byte  = 8bits  = 1字节  Byte     -128~127     单精度  float  = 32bits = 4字节   Float
            短整型  short = 16bits = 2字节  Short    -128~127     双精度  double = 64bits = 8字节   Double
            整形    int   = 32bits = 4字节  Integer  -128~127
            长整型  long  = 64bits = 8字节  Long     -128~127
        b.非数字
            字符型   char = 16bit  = 2字节  Character
            布尔型   boolean = 1bit/未定    Boolean  (0 false,1 true)
    c.操作
        a.装箱:基础类型 -> 包装类
            int a = 10;                             String a = "123";
            Integer b = Integer.valueOf(a);         Integer b = Integer.valueOf(a);
        b.拆箱:包装类 -> 基础类型
            Integer a = 10;                         Integer a = 10;
            int b = a.intValue();                   long b = a.longValue();
        c.自动装箱、自动拆箱
            int a = 10;
            Integer b = new Integer(20);
            -------------------------------------------------------------------------------------------------
            // Integer -> int,反编译可知,【自动拆箱】底层调用【Integer类 实现Number抽象类的 intValue() 方法】
            a = b;
            -------------------------------------------------------------------------------------------------
            // int -> Integer,反编译可知,【自动装箱】底层调用【Integer类 的 静态 valueOf(int i) 方法】
            b = a;
        d.类型转换
            // 将long型转化为int型                   // 将Long型转化为Integer型(自动拆箱、自动装箱)
            long a = 10;                            Long a = 10L;                    int a = 10;
            int b = (int)a;                         Integer b = a.intValue();
            -------------------------------------------------------------------------------------------------
            // 将int型转化为long型                   // 将Integer型转化为Long型(自动拆箱、自动装箱)
            int a = 10;                             Integer a = 10;
            long b = (long)a;                       Long b = a.longValue();
        e.缓冲区:如果要“装箱”的数字在[-128,127]以内,则直接从缓冲区获取,否则,需要使用 NEW 关键词创建一个新对象
            a.int是基本类型,【基本数据类型 == 比较数值】
                int a = 100;
                int b = 100;
                System.out.println(a == b);         // 结果为true
                int c = 1000;
                int d = 1000;
                System.out.println(c == d);         // 结果为true
            b.Integer是引用类型,【引用数据类型 == 比较地址】,IntegerCache定义范围[-128,127]
                Integer a = 100;
                Integer b = 100;
                System.out.println(a == b);         // 结果为true
                Integer c = 1000;
                Integer d = 1000;
                System.out.println(c == d);         // 结果为false
                ---------------------------------------------------------------------------------------------
                public static Integer valueOf(int i) {
                    if (i >= IntegerCache.low && i <= IntegerCache.high)
                        return IntegerCache.cache[i + (-IntegerCache.low)];
                    return new Integer(i);
                }
            c.Double是引用类型,【引用数据类型 == 比较地址】,没有Double对应的常量池
                Double a = 100.0;
                Double b = 100.0;
                System.out.println(a == b);         // 结果为false
                Double c = 200.0;
                Double d = 200.0;
                System.out.println(c == d);         // 结果为false
                ---------------------------------------------------------------------------------------------
                public static Double valueOf(double d) {
                    return new Double(d);
                }
            d.Boolean是引用类型,【引用数据类型 == 比较地址】,没有Boolean对应的常量池
                Boolean a = false;
                Boolean b = false;
                System.out.println(a == b);         // 结果为true
                Boolean c = true;
                Boolean d = true;
                System.out.println(c == d);         // 结果为true
                ---------------------------------------------------------------------------------------------
                public static Boolean valueOf(boolean b) {
                    return (b ? TRUE : FALSE);
                }
    d.示例1
        a.代码
            Integer i1 = 10;                        // 自动装箱
            Integer i2 = new Integer(10);           // 非自动装箱
            Integer i3 = Integer.valueOf(10);       // 非自动装箱
            int i4 = new Integer(10);               // 自动拆箱
            int i5 = i2.intValue();                 // 非自动拆箱
            ---------------------------------------------------------------------------------------------
            Integer a = 1;
            Integer b = 2;
            Integer c = 3;
            Integer d = 3;
            Integer e = 321;
            Integer f = 321;
            Long g = 3L;
            Long h = 2L;
            System.out.println(c==d);
            System.out.println(e==f);
            System.out.println(c==(a+b));
            System.out.println(c.equals(a+b));
            System.out.println(g==(a+b));
            System.out.println(g.equals(a+b));
            System.out.println(g.equals(a+h));
        b.结果
            true    IntegerCache定义范围[-128,127]
            false   没有Double对应的常量池
            true    a+b包含算数运算,触发拆箱,调用intValue()方法,因此进行数值上的比较
            true    先自动拆箱(a+b各自调用intValue后加法运算),再自动装箱(调用Integer.valueOf),最后equals
            true    a+b包含算数运算,触发拆箱,调用intValue()方法,因此进行数值上的比较
            false   g.equals(a+b),g(Long包装类),a+b(Integer包装类:自动装箱调用Integer.valueOf)
            true    g.equals(a+b),g(Long包装类),a+h(Long包装类:自动装箱调用Long.valueOf)
        c.分析
            a."=="运算符
                两边的操作符:均为包装类的引用,【比较地址,是否为同一个对象】
                两边的操作符:有一个是表达式(算术运算),【比较数值,触发自动拆箱过程】
            b.equals
                包装器类型,equals方法【不会进行类型转换,即不会触发自动拆箱过程】
        d.场景
            a.场景1
                调用一个【参数为Object类型】的“方法”,因此传入int类型时,【触发Integer.valueOf自动装箱】
            b.场景2
                调用一个【参数为Object类型】的“非泛型的容器”,因此传入int类型时,【触发Integer.valueOf自动装箱】
            c.建议
                优点:默认情况下,使用【原始类型/对象类型】更加简单
                缺点:如果在一个循环体中,会创建无用的中间对象,这样会增加GC压力,拉低程序的性能
                建议:装箱操作会创建对象,频繁的装箱操作会造成不必要的内存消耗,【写循环时,应当尽量避免装箱操作】
                -----------------------------------------------------------------------------------------
                Long l = 0L;
                for(int i = 0; i < 50000; i++) {
                    l += 1L;
                }
    e.谈谈Integer i = new Integer(123) 和 Integer i = 123 区别
        a.自动装箱、自动拆箱
            第一种:不会触发自动装箱
            第二种:会触发自动装箱
        b.执行效率和资源占用上的区别
            第二种方式的执行效率和资源占用,在一般性情况下要优于第一种情况(注意,这并不是绝对的)
    f.谈谈Integer i = new Integer(123) 和 Integer.valueOf(123) 区别
        a.区别
            new Integer(123):每次都会新建一个对象;
            Integer.valueOf(123):会使用缓存池中的对象,多次调用会取得同一个对象的引用
        b.缓存池
            在 Java 8 中,Integer 缓存池的大小默认为 -128~127
            编译器会在自动装箱过程调用valueOf()方法,因为会使用自动装箱来创建,那么就会引用相同的对象
    g.为什么Double类的valueOf方法,与Integer类的valueOf方法不同的实现?
        a.理由
            很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。
        b.valueOf()类似
            【Byte、Short、Integer、Long类】【Character类】:6个方法,它们实现valueOf()方法类似
            【Float、Double类】:2个方法,它们实现valueOf()方法类似
    h.【Byte、Short、Integer、Long类】,【Float、Double类】,【Character、Boolean类】
        a.缓冲区
            有【ByteCache、ShortCache、IntegerCache、LongCache类,Character类】
            无【Float、Double类】
            无【Boolean类】
        b.继承Number抽象类:【重写byteVaule、shortValut,实现intVaule、longValue、floatValue、doubleValue】
            有【Byte、Short、Integer、Long类,Float、Double类】
            无【Character、Boolean类】
            【“char可以转换为int,但Charcter无法转换为Integer”,可以转换为它的父类】
        c.实现Comparable接口:【调用Integer的静态compare() 实现 Comparable接口的compareTo()】
            都有
        d.继承Object默认父类:【重写String toString()、int hashCode(int value)、boolean equals(Object obj)】
            都有
        e.常见静态方法:int parseXxx(String s)、Integer valueOf(String s)
            有【Byte、Short、Integer、Long类】
            有【Float、Double类】
            无【Character类】,Character类有valueOf(char c)
            有【Boolean类】
        f.常见静态方法:Integer decode(String s)
            有【Byte、Short、Integer、Long类】
            无【Float、Double类】
            无【Character、Boolean类】
        g.常见静态方法:sum max min
            无【Byte、Short类】
            有【Integer、Long类】
            有【Float、Double类】
            无【Character、Boolean类】

03.String类的转换
    a.其他类型 -> 字符串
        【自动转换】:xxx + ""
        【调用Object】:xxx.toString()
        【调用String】:String.valueOf(XXX xxx)
    b.字符串 -> 其他类型
        【调用继承/重写的Object.toString()方法】:xxx.toString()
        【将"字符串 -> 包装类",再转换为其他类型】:new Float("1.23").doubleValue()
        【调用Byte、Short、Integer、Long类,Float、Double类的parseXxx(String s)】:Integer.parseInt("123");

04.Date类的转换
    a.构造方法
        Date() {System.currentTimeMillis())}                            // 当前时间
        Date(long date)                                                 // 距1970年1月1日0时0分0秒的毫秒数
        Date(int year, int month, int date)                             // 以int型表示年、月、日
        Date(int year, int month, int date, int hrs, int min)           // 以int型表示年、月、日、时、分
        Date(int year, int month, int date, int hrs, int min, int sec)  // 以int型表示年、月、日、时、分、秒
    b.方法
        getTime()、getYear()、getMonth()、getDate()、getHours()、getMinutes()、getSeconds()、getDay()
    c.参照SimpleDateFormat类中的注释的指定格式
        Date date = new Date();
        System.out.println("默认调用toString()方法,直接输出:" + date);
        System.out.println("调用getTime(),时间戳:" + date.getTime());
        System.out.println("调用SimpleDateFormat,年:" + new SimpleDateFormat("yyyy").format(date));
        System.out.println("调用SimpleDateFormat,年:" + new SimpleDateFormat("MM").format(date));
        System.out.println("调用SimpleDateFormat,年:" + new SimpleDateFormat("DD").format(date));
        System.out.println("调用SimpleDateFormat,年:" + new SimpleDateFormat("yyyyMMDD").format(date));
        System.out.println("调用SimpleDateFormat,年:" + new SimpleDateFormat("yyyy-MM-DD").format(date));

2.3 常见转换

00.图示
                     23.4 + ""
                 String.valueOf(3.4f)                   包装类.valueOf()
                         -->                                  -->
    基本类型                             String                                  包装类型
                         <--                                  <--
              包装类的静态方法:包装类.parseXxx()        包装类对象.toString()

                                   构造器:new Integer(11)
                                   自动装箱
              ---------------------------------------------------------------->
              <----------------------------------------------------------------
                                   包装类:包装类.xxxValue()
                                   自动拆箱

01.String.valueOf():将 基本类型 转换为 String字符串
    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
    -----------------------------------------------------------------------------------------------------
    public String toString() {
        return this;                                        --返回此对象本身(它已经是一个字符串!)
    }
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();     --对 obj 判断是否为 null
    }
    虽然toString()和valueOf()都可以转化为String类型,但【String.valueOf()传入null时,返回的是“null”】

02.包装类.valueOf():将 String字符串 转换为 包装类
    Byte.valueOf(String s)                                  --将 String 转换为 Byte
    Short.valueOf(String s)                                 --将 String 转换为 Short
    Integer.valueOf(String s)                               --将 String 转换为 Integer
    Long.valueOf(String s)                                  --将 String 转换为 Long
    Float.valueOf(String s)                                 --将 String 转换为 Float
    Double.valueOf(String s)                                --将 String 转换为 Double
    Character.valueOf(char c)                               --将 char   转换为 Character
    Boolean.valueOf(String s)                               --将 String 转换为 Boolean

03.包装类.parseXxx():将 String字符串 转换为 基本类型
    Byte.parseByte(String s)                                --将 String 转换为 byte
    Short.parseShort(String s)                              --将 String 转换为 short
    Integer.parseInt(String s)                              --将 String 转换为 int
    Long.parseLong(String s)                                --将 String 转换为 long
    Float.parseFloat(String s)                              --将 String 转换为 float
    Double.parseDouble(String s)                            --将 String 转换为 double
    Character.无                                            --将 String 转换为 char
    Boolean.parseBoolean(String s)                          --将 String 转换为 boolean

04.String转成Integer/int的方法
    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

05.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测试,证明此方法效率更高

2.4 判等问题1

01.==和equals()有什么区别?
    ==运算符:
        作用于基本数据类型时,是比较两个数值是否相等;
        作用于引用数据类型时,是比较两个对象的内存地址是否相同,即判断它们是否为同一个对象;
    equals()方法:
        没有重写时,Object默认以 == 来实现,即比较两个对象的内存地址是否相同;
        进行重写后,一般会按照对象的内容来进行比较,若两个对象内容相同则认为对象相等,否则认为对象不等。

02.说一说hashCode()和equals()的关系
    hashCode()用于获取哈希码(散列码),eauqls()用于比较两个对象是否相等,它们应遵守如下规定:
    如果两个对象相等,则它们必须有相同的哈希码。
    如果两个对象有相同的哈希码,则它们未必相等。

03.判断问题
    a.问题
        ==:关系运算符,对象内存地址,【原生数据类型(地址中储存数值),引用数据类型(地址中存储指向堆中的地址)】
        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.比较1
        String name = "zs";
        String pwd = "123";
        if(name.equals("zs") && pwd.equals("123") {
            System.out.println("成功!")
        }
        运算符“==”不能应用于“java.lang.String”、“int”,应当使用"equals"
    c.比较2
        String类,重写了Object类中的equals、hashCode方法,并且在Object类上,进行了各自的扩展;
        例如,equals方法不仅仅是对【先对象的引用地址进行判断,再对"字符"进行逐个比对判断】,源码如下:
        -------------------------------------------------------------------------------------------------
        public boolean equals(Object obj) {                     // Object类
            return (this == obj);
        }
        -------------------------------------------------------------------------------------------------
        public boolean equals(Object anObject) {                // String类
            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;

04.面试题三
    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()方法验证

05.编写一个完美的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方法的规定(相等对象必须具有相等的哈希码)

2.5 判等问题2

01.包装类
    Integer a = 127;                    // 自动装箱,调用Integer.valueOf(127),缓存[-128,127]
    Integer b = 127;                    // 自动装箱,调用Integer.valueOf(127),缓存[-128,127]
    System.out.println(a == b);         // true
    ---------------------------------------------------------------------------------------------------
    Integer c = 128;                    // 自动装箱,调用Integer.valueOf(128),缓存[-128,127]
    Integer d = 128;                    // 自动装箱,调用Integer.valueOf(128),缓存[-128,127]
    System.out.println(c == d);         // false
    ---------------------------------------------------------------------------------------------------
    Integer e = 127;                    // 自动装箱,调用Integer.valueOf(127),缓存[-128,127]
    Integer f = new Integer(127);       // new instance
    System.out.println(e == f);         // false(比较一个新对象和一个来自缓存的对象)
    ---------------------------------------------------------------------------------------------------
    Integer g = new Integer(127);       // new instance
    Integer h = new Integer(127);       // new instance
    System.out.println(g == h);         // false(两个对象的地址不同)
    ---------------------------------------------------------------------------------------------------
    Integer i = 128;                    // 自动装箱,调用Integer.valueOf(128),缓存[-128,127]
    int j = 128;                        // 基本类型
    System.out.println(i == j);         // true(比较数值)

02.字符串
    String a = "1";
    String b = "1";
    System.out.println(a == b);         // true(字符串常量池)
    ---------------------------------------------------------------------------------------------------
    String c = new String("2");
    String d = new String("2");
    System.out.println(c == d);         // false(两个对象的地址不同)
    ---------------------------------------------------------------------------------------------------
    String e = new String("3").intern();
    String f = new String("3").intern();
    System.out.println(e == f);         // true(两个对象的地址不同,但intern()会同样指向常量池)
    ---------------------------------------------------------------------------------------------------
    String g = new String("4");
    String h = new String("4");
    System.out.println(g.equals(h));    // true(String类的equals方法:先比较对象地址,再逐步比较"字符")

03.实现equals:常见类(Integer、String)已重写该方法,自定义类(MyClass)默认调用Object中的equals方法
    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方法的规定(相等对象必须具有相等的哈希码)

04.compareTo 和 equals 的逻辑一致性
    a.错误示例
        @Data
        @AllArgsConstructor
        class Student implements Comparable<Student> {

            private int id;
            private String name;

            @Override
            public int compareTo(Student other) {
                int result = Integer.compare(other.id, id);
                if (result == 0) { log.info("this {} == other {}", this, other); }
                return result;
            }
        }
        ----------------------------------------------------------------------------------------------
        public class Demo {
            public static void main(String[] args) {
                List<Student> list = new ArrayList<>();
                list.add(new Student(1, "zhang"));
                list.add(new Student(2, "wang"));
                Student student = new Student(2, "li");
                int index1 = list.indexOf(student);                     // ArrayList.indexOf()

                Collections.sort(list);                                 // Comparable,自然排序

                int index2 = Collections.binarySearch(list, student);   // binarySearch()内部调用Comparable

                log.info("index1 = " + index1); // indexOf 的结果没问题,列表中搜索不到id为2、name是li的学生
                log.info("index2 = " + index2); // binarySearch返回了索引1,代表是id为2,name是 wang
            }
        }
    b.正确示例:确保 compareTo 的比较逻辑和 equals 的实现一致
        @Data
        @AllArgsConstructor
        class StudentRight implements Comparable<StudentRight> {

            private int id;
            private String name;

            @Override
            public int compareTo(StudentRight other) {
                return Comparator.comparing(StudentRight::getName)
                    .thenComparingInt(StudentRight::getId)
                    .compare(this, other);
            }
        }

05.小心Lombok生成代码的“坑”:默认使用非静态,非瞬态的属性
    a.@Data
        默认生成的equals和hashcode方法(不包含继承的父类属性方法),不能对“子类”生成,不适合map、set等集合使用
    b.@EqualsAndHashCode
        默认实现没有使用父类属性,若想调用父类的方法,则需要指定callSuper,@EqualsAndHashCode(callSuper=true)

3 选择、循环、中断

3.1 总结

01.选择语句(数轴):if与else对立;switch(类似if...else);分支多(switch优于if...else)
    a.if
        简单if:赋值=,而非 关系==
        多重if:大于,越大越优秀;小于,越小越优先;【if...else if...推荐改写为三目运算符 boolean ? ture: false;】
        嵌套if:数轴
        区别:if判断一件事/多件事;多重if判断一件事;嵌套if判断多件事
    b.switch:类似if..else
        a.用法
            switch      使用“rank=2 与 全部case(1,2,3,4,5...)”进行匹配;
            switch      从匹配“case=2”开始,再接着匹配“case=3, 4, 5”,“最后运行defalt语句”,不遇到break不会停止;
            switch      如果没有匹配项,则执行defalt语句;
        b.总结
            switch      支持的表达式:byte,short,char —> int,String,enum
            case        必须是常量,int类型,case值不能重复
            default     可省略,default要放在最后
            break       跳出整个switch语句,并且继续执行该循环下面的语句,直至循环结束
            结束标志     break、最后的}
    c.多重if结构和switch有什么区别?
        a.switch
            switch只能判断离散的单点值,例如1、2、3..(注意:1和2之间,有无穷多个小数)
        b.多重if
            多重if既能判断离散的单点值,也能判断区间值(注意:>90就是一个区间值)
        c.场景
            能用switch实现“如果<0,标记寒冷”吗?不能,但是多重if可以

02.循环语句:循环来自生活中的重复
    a.类型
        while       先判断后执行                         【if不更新变量,while/do...while/for更新变量】
        do...while  先执行后判断                         【需要即使不满足条件,也至少执行一次】
        for         已知循环次数,已知始末条件            【for有两种类型,可以等价替换为while、do...while】
        for         增加型for循环,变量值相当于num[i]     【增强型for循环,优于传统for循环】
        for         不要使用for循环遍历容器元素,删除元素  【正确用法:遍历容器的迭代器(Iterator),删除元素】
    b.for是while的变体
        a.示例1
            int[] nums = new int[]{1, 2, 3, 4}
            for (int i=0; i<num.length; i++) {          for (初始值; 循环条件; 更新变量) {
                System.out.println(num[i]);                 循环操作
            }                                           }
        b.示例2
            int[] nums = new int[]{1, 2, 3, 4}          // JAVA1.5引入增加型for循环,变量值相当于num[i]
            for (int i: nums) {                         for (元素类型 变量值: 数组){
                System.out.println(i)                       循环操作
            }                                           }

03.中断语句
    a.break
        【switch/当前循环】,【跳出最里层循环,并且继续执行该循环下面的语句】
    b.continue
        【当前循环】,【让程序立刻跳转到下一次循环的迭代,for跳到更新语句,while/do while跳到布尔表达式判断】
    c.return
        【函数语句】,【跳出整个函数体,函数体后面的部分不再执行】
    d.总结
        a.break
            当前循环,内部for【终止】,外部for【无影响】;【if不更新变量,while/do...while/for更新变量】
        b.continue
            当前循环,内部for【跳过内层3】,外部for【无影响】;【if不更新变量,while/do...while/for更新变量】
        c.return
            【break、continue仅对当前层次的循环有效】;【return直接跳出函数体(包括全部循环、函数体后面的语句)】

3.2 选择语句

01.简单if(数轴):if与else对立;switch(类似if...else);分支多(switch优于if...else)
    boolean flag = 3<2;
    if (flag = true) {                              // 注意此处是 赋值=,而非 关系==
        System.out.println("a"):
    } else {
        System.out.println("b");
    }

02.多重if
    a.示例1
        int score = 88;
        if (score >= 90) {                          // 大于,越大越优秀(大于,从大到小)
            System.out.println("优秀");
        } else if (score >= 80) {
            System.out.println("良好");
        } else if (score >= 60) {
            System.out.println("及格");
        } else {
            System.out.println("不及格");
        }
        -----------------------------------------------------------------------------------------------------
        int score = 88;
        if (score < 60) {                           // 小于,越小越优先(小于,从小到大)
            System.out.println("不及格");
        } else if (score < 80) {
            System.out.println("及格");
        } else if (score < 90) {
            System.out.println("良好");
        } else {
            System.out.println("优秀");
        }
    b.示例2
        int bug = 5
        if (bug >= 10) {                            // 大于,越大越优秀(大于,从大到小)
            System.out.println("D");
        } else if (bug >= 5) {
            System.out.println("C");
        } else if (bug >= 3) {
            System.out.println("B");
        } else {
            System.out.println("A");
        }
        -----------------------------------------------------------------------------------------------------
        int bug = 5
        if (bug < 3) {                              // 小于,越小越优先(小于,从小到大)
            System.out.println("A");
        } else if (bug < 5) {
            System.out.println("B");
        } else if (bug < 10) {
            System.out.println("C");
        } else {
            System.out.println("D");
        }

03.嵌套if
    // 10秒以内进入决赛
    double sec = 9.8;
    char sex = '女';
    if (sec <= 10) {
        if(sex == '男') {
            System.out.println("进入男子组决赛...");
        } else {
            System.out.println("进入女子组角色...");
        }
    } else {
        System.out.println("淘汰...");
    }
    ---------------------------------------------------------------------------------------------------------
    进入女子组角色...

04.区别
    a.if与多重if区别
        a.if判断一件事/多件事
            int rank = 1;
            if (rank == 1) {
                System.out.println("夏令营");
            }
            if (rank == 2) {
                System.out.println("笔记本电脑");
            }
            if (rank == 3) {
                System.out.println("鼠标垫");
            }
            System.out.println("无论rank为多少,都会执行");
            -------------------------------------------------------------------------------------------------
            夏令营
            无论rank为多少,都会执行
        b.多重if判断一件事
            int rank = 1;
            if (rank == 1) {
                System.out.println("夏令营");
            } else if (rank == 2) {
                System.out.println("笔记本电脑");
            } else if (rank == 3) {
                System.out.println("鼠标垫");
            } else {
                System.out.println("不奖励");
            }
            -------------------------------------------------------------------------------------------------
            夏令营
    b.多重if与嵌套if区别
        a.标志
            是否判断同一件事情
        b.结果
            如果是同一件事情,用多重if;否则(非同一件事情),用嵌套if

05.switch(类似if..else)
    a.示例1
        int rank = 2;
        switch (rank) {
            case 1:
                System.out.println("笔记本电脑");
            case 2:
                System.out.println("鼠标垫");
            case 3:
                System.out.println("夏令营");
            case 4:
                System.out.println("测试");
                break;
            default:
                System.out.println("不奖励");
                break;
        }
        -----------------------------------------------------------------------------------------------------
        鼠标垫
        夏令营
        测试
    b.示例2
        int rank = 2;
        switch (rank) {
            case 1:
                System.out.println("笔记本电脑");
            case 2:
                System.out.println("鼠标垫");
            case 3:
                System.out.println("夏令营");
            case 4:
                System.out.println("测试");
            default:
                System.out.println("不奖励");
                break;
        }
        -----------------------------------------------------------------------------------------------------
        鼠标垫
        夏令营
        测试
        不奖励
    c.示例3
        System.out.println("请输入月份:");
        Scanner input = new Scanner(System.in);
        if (input.hasNextInt()) {
            int month = input.nextInt();
            switch (month) {
                case 1:
                case 3:
                case 5:
                case 7:
                case 8:
                case 10:
                case 12:
                    System.out.println("该月是大月...");
                    break;
                default:
                    System.out.println("该月是小月...");
            }
        } else {
            System.out.println("请输入数字!!!");
        }
        -----------------------------------------------------------------------------------------------------
        请输入月份:
        5
        该月是大月...
        -----------------------------------------------------------------------------------------------------
        请输入月份:
        4
        该月是小月...
        -----------------------------------------------------------------------------------------------------
        请输入月份:
        你好
        请输入数字!!!
    d.用法
        switch      使用“rank=2 与 全部case(1,2,3,4,5...)”进行匹配;
        switch      从匹配“case=2”开始,再接着匹配“case=3, 4, 5”,“最后运行defalt语句”,不遇到break不会停止;
        switch      如果没有匹配项,则执行defalt语句;
    e.总结
        switch      支持的表达式:byte,short,char —> int,String,enum
        case        必须是常量,int类型,case值不能重复
        default     可省略,default要放在最后
        break       跳出整个switch语句,并且继续执行该循环下面的语句,直至循环结束
        结束标志     break、最后的}

3.3 循环语句

01.类型:循环来自生活中的重复
    while       先判断后执行                         【if不更新变量,while/do...while/for更新变量】
    do...while  先执行后判断                         【需要即使不满足条件,也至少执行一次】
    for         已知循环次数,已知始末条件            【for有两种类型,可以等价替换为while、do...while】
    for         增加型for循环,变量值相当于num[i]     【增强型for循环,优于传统for循环】
    for         不要使用for循环遍历容器元素,删除元素  【正确用法:遍历容器的迭代器(Iterator),删除元素】

02.for是while的变体
    int sum = 0;                                    int sum = 0;
    int i = 1;
    while (i < 100) {                               for (int i = 1; i < 100; i++) {
        sum = sum + i;                                  sum = sum + i;
        i++;
    }                                               }

03.增强For循环,也叫foreach
    a.传统For循环
        int[] nums = new int[]{1, 2, 3, 4}
        for (int i=0; i<num.length; i++) {          for (初始值; 循环条件; 更新变量) {
            System.out.println(num[i]);                 循环操作
        }                                           }
    b.增强For循环
        int[] nums = new int[]{1, 2, 3, 4}          // JAVA1.5引入增加型for循环,变量值相当于num[i]
        for (int i: nums) {                         for (元素类型 变量值: 数组/集合){
            System.out.println(i)                       循环操作
        }                                           }

04.核心:重复
    a.第一步
        int sum = 0;        // 0
        sum = sum + 1;      // 0 + 1
        sum = sum + 2;      // 0 + 1 + 2
        sum = sum + 3;      // 0 + 1 + 2 + 3
        sum = sum + 4;      // 0 + 1 + 2 + 3 + 4
    b.第二步
        int sum = 0;
        int i = 1;
        sum = sum + i;
        i++;
        sum = sum + i;
        i++;
    c.第三步:计算100以内的数之和
        int sum = 0;
        int i = 1;
        while (i <= 100) {
            sum = sum + i;
            i++;
        }
        System.out.println(sum);
    d.第四步:计算100以内的偶数之和
        int sum = 0;
        int i = 1;
        while (i <= 100) {
            if (i % 2 == 0) {
                sum = sum + i;
            }
            i++;
        }
        System.out.println(sum);
    e.第五步:计算100以内不能被3整除的数之和【while】
        int sum = 0;
        int i = 1;
        while (i < 100) {
            if (i % 3 != 0) {
                sum = sum + i;
            }
            i++;
        }
        System.out.println(sum);
    f.第六步:计算100以内不能被3整除的数之和【do...while】
        int sum = 0;
        int i = 1;
        do {
            if (i % 3 != 0) {
                sum = sum + i;
            }
            i++;
        } while (i<100)
        System.out.println(sum);
    g.第七步:计算100以内不能被3整除的数之和【for】
        int sum = 0;
        for (int i = 1; i < 100; i++) {
            if (i % 3 != 0) {
                sum = sum + i;
            }
        }
        System.out.println(sum);

3.4 中断语句

01.break:【switch/当前循环】,【跳出最里层循环,并且继续执行该循环下面的语句】
    public static void main(String args[]) {
        for (int i=0; i<5; i++) {
            if (i == 2) {
                break;
            }
            System.out.println(i);
        }
        System.out.println("循环外的语句");
    }
    ---------------------------------------------------------------------------------------------------------
    0
    1
    循环外的语句

02.continue:【当前循环】,【让程序立刻跳转到下一次循环的迭代,for跳到更新语句,while/do while跳到布尔表达式判断】
    public static void main(String args[]) {
        for (int i=0; i<5; i++) {
            if (i == 2) {
                continue;
            }
            System.out.println(i);
        }
        System.out.println("循环外的语句");
    }
    ---------------------------------------------------------------------------------------------------------
    0
    1
    3
    4
    循环外的语句

03.return:【函数语句】,【跳出整个函数体,函数体后面的部分不再执行】
    public static void main(String args[]) {
        for (int i=0; i<5; i++) {
            if (i == 2) {
                return;
            }
            System.out.println(i);
        }
        System.out.println("循环外的语句");
    }
    ---------------------------------------------------------------------------------------------------------
    0
    1

04.区别
    a.break
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                System.out.print("外层:" + i);
                for (int j = 0; j < 7; j++) {
                    if (j == 3) {
                        break;
                    }
                    System.out.print("\t内层:" + j);
                }
                System.out.print("\n");
            }
        }
        -----------------------------------------------------------------------------------------------------
        外层:0    内层:0    内层:1    内层:2
        外层:1    内层:0    内层:1    内层:2
        外层:2    内层:0    内层:1    内层:2
        外层:3    内层:0    内层:1    内层:2
        外层:4    内层:0    内层:1    内层:2
        -----------------------------------------------------------------------------------------------------
        当前循环,内部for循环【终止】,外部for循环【无影响】;【if不更新变量,while/do...while/for更新变量】
    b.continue
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                System.out.print("外层:" + i);
                for (int j = 0; j < 7; j++) {
                    if (j == 3) {
                        coutinue;
                    }
                    System.out.print("\t内层:" + j);
                }
                System.out.print("\n");
            }
        }
        -----------------------------------------------------------------------------------------------------
        外层:0    内层:0    内层:1    内层:2    内层:4    内层:5    内层:6
        外层:1    内层:0    内层:1    内层:2    内层:4    内层:5    内层:6
        外层:2    内层:0    内层:1    内层:2    内层:4    内层:5    内层:6
        外层:3    内层:0    内层:1    内层:2    内层:4    内层:5    内层:6
        外层:4    内层:0    内层:1    内层:2    内层:4    内层:5    内层:6
        -----------------------------------------------------------------------------------------------------
        当前循环,内部for循环【跳过内层3】,外部for循环【无影响】;【if不更新变量,while/do...while/for更新变量】
    c.return
        public static void main(String[] args) {
            for (int i = 0; i <        	 5; i++) {
                System.out.print("外层:" + i);
                for (int j = 0; j < 7; j++) {
                    if (j == 3) {
                        return;
                    }
                    System.out.print("\t内层:" + j);
                }
                System.out.print("\n");
            }
        }
        -----------------------------------------------------------------------------------------------------
        外层:0    内层:0    内层:1    内层:2
        -----------------------------------------------------------------------------------------------------
        【break、continue仅对当前层次的循环有效】;【return直接跳出函数体(包括全部循环、函数体后面的语句)】

4 字符串

4.1 String基础

01.流程:默认【整数:0,小数:0.0,字符:'\u0000',布尔:flase】【引用:null / 对象内部默认属性值 / ""】
    a.JVM内存空间
        Code  代码区:类的定义,静态资源   Student.class
        Heap  堆空间:分配对象(分配实例) new Student()
        Stack 栈空间:临时变量            Student stu
    b.步骤
        JVM 加载 Student.class 到 Code 代码区
        new Student() 会在堆空间分配空间,并创建一个 Student 实例
        将 Student 堆中地址 赋值给 栈空间的stu变量

02.对于非基本类型,即"对象类型/引用类型",默认值有如下
    a.只声明,不NEW实例化:对象默认“NULL”,且为全局变量
        Person per;                         // per默认null
        String str;                         // str默认null
    b.有声明,并NEW实例化:对象默认“非NULL”,且“对象内部的属性值,都是【对应数据类型的默认值】”
        Person per = new Person();          // per默认:name为null,age为0
        String str = new String();          // str默认"",创建空字符串
        String str = new String("");        // str会创建新的空字符串,是空串"",不是null

03.String的字符串拼接"+"(JAVA中仅有的两个重载过的运算符:String类中的"+"、"+=")
    a.提问
        String在使用时,字符串拼接"+"是怎么回事?
    b.回答
        Java语言为字符串连接运算符(+)提供特殊支持,并将其他对象转换为字符串
        “+”的拼接:StringBuilder类(反编译)的非静态 append() 方法
        “+”的转换:String类 重写 Object类的 toString 方法

04.String的创建对象
    a.方式一:通过字符串常量的创建
        a.提问
            String str = “abc”;,在内存中创建了几个对象?
        b.回答
            JVM会检查“该对象是否在字符串常量池”,
            若存在,则直接返回该对象引用;
            若不存在,则新的字符串将在常量池中被创建,再返回该对象引用
    b.方式二:通过NEW形式的创建
        a.提问
            String str = new String("abc");,在内存中创建了几个对象?
        b.回答
            【在内存中创建两个对象,一次关于堆内存,一次关于常量池,堆内存对象是常量池对象的一个拷贝副本】
            String(String original):源码解读,新创建的字符串是参数字符串的副本
        c.过程
            首先,在【堆内存中,创建一个匿名对象"abc",并且"abc"进行入池操作】,
            然后,使用 NEW 关键字,在【堆内存中开辟一块新空间,将"abc"存进去,然后把 地址 返给栈内存中的 st1】,
            最后,由于匿名对象"abc"没有被栈中变量使用,变为垃圾对象而被GC回收
            --------------------------------------------------------------------------------------------
            首先,在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,"abc" 将会在常量池中创建;
            然后,使用 NEW 关键字,JVM调用String的构造函数在堆内存创建对象,同时引用常量池中的"abc"字符串,
            最后,str将最终引用String对象
    c.总结
        使用String字符串时,
        如果直接使用"hello",即String str1 = "hello",则从常量池中寻找;
        如果使用NEW关键字,即String str3 = new String("hello"),则从堆中寻找;
        (而堆中又会从常量池中寻找,如果常量池中不存在,则在常量池创建,并引用常量池中的字符串)
        (引用:使用常量池中的内存地址)
        建议:将频繁使用的常量放入常量池,而非【重复使用NEW关键字开辟内存空间】,以提高空间使用率

05.原始String对象的内容是否改变
    a.提问
        字符串使用 += 赋值后,原始的String对象中的内容会改变吗?
    b.回答
        字符串内容不会改变,但对象的引用可以改变
    c.解释
        public class StringDemo {
            public static void main(String[] args) {
                String s = "Hello";
                s += "World";
                System.out.println("s:" + s);           // 结果为s:HelloWorld
            }
        }
        final 修饰的类【不能被继承】,是指【字符串对象本身不能改变】,不等同于【对象的引用不能改变】
        上述过程中,字符串本身的内容是没有任何变化的,而是分别创建了三块内存空间:(Hello) (World) (HelloWorld)
        Hello + World 拼接为 HelloWorld 时,s 无法指向原来的 Hello 对象,而改变指向为 HelloWorld

06.StringBuffer、StringBuilder:均继承AbstractStringBuilder抽象类,【线程是否安全】是最大区别,其他几乎一模一样
    a.字符串拼接
        +               循环内,减少“+”,推荐StringBuild、StringBuffer的append
        线程安全         StringBuffer       public synchronized StringBuffer insert()
        线程不安全       StringBuilder      public StringBuilder insert()
    b.字符数组:char value[]与char[] value等价
        String          不可变的字符数组    private final char value[];             不可变,拼接等会产生新对象
        StringBuffer    可变的字符数组      char[] value; -> AbstractStringBuilder  JDK9以后底层用byte[]
        StringBuilder   可变的字符数组      char[] value; -> AbstractStringBuilder  JDK9以后底层用byte[]
    c.线程
        String          线程安全           不可变字符串(private final char value[];)
        StringBuffer    线程安全           内部使用 synchronized 进行同步
        StringBuilder   线程不安全         去掉了线程安全,有效减少了内存开销,字符串拼接的首选
    d.使用
        String          线程安全           字符串的修改不频繁
        StringBuffer    线程安全           字符串的修改频繁,且“字符串是全局变量,或需要多线程支持”
        StringBuilder   线程不安全         字符串的修改频繁,且“字符串是局部变量,或需要单线程支持”

07.字符串拼接
    + 运算符:如果拼接的都是字符串直接量,则适合使用 + 运算符实现拼接;
    StringBuilder:如果拼接的字符串中包含变量,并不要求线程安全,则适合使用StringBuilder;
    StringBuffer:如果拼接的字符串中包含变量,并且要求线程安全,则适合使用StringBuffer;
    String类的concat方法:如果只是对两个字符串进行拼接,并且包含变量,则适合使用concat方法;

4.2 String不可变

01.常用结论1
    a.String类
        public final class String
            implements java.io.Serializable, Comparable<String>, CharSequence {
            private final char value[];
            ...
        }
        public final class StringBuffer
            extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
            char[] value; -> AbstractStringBuilder
            ...
        }
        public final class StringBuilder
            extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
            char[] value; -> AbstractStringBuilder
            ...
        }
        final修饰String类,表示【不可继承类】,是指【字符串对象本身不能改变,不等同于“对象的引用不能改变”】
        final修改char[]数组,表示【数组存储于char[]数组】,表示【String对象不可被更改】
        ------------------------------------------------------------------------------------------------
        为什么Java要这样设计?
        ①安全性:String不可变性保证“参数”不可变,如果网络连接中String参数可变,那么连接的地址可能被篡改
        ②hash值:String用做HashMap的key,只需对hash进行一次计算
        ②字符串常量池:如果一个String对象已经被创建,直接从字符串常量池中引用。只有String是不可变的,才可能实现
        ④线程安全:不可变性,天生具备线程安全
    b.String池化
        String s1 = new String("");         // 第一种不会入池
        String s2 = "";                     // 第二种看情况而定(等号右边如果是常量则入池,非常量则不入池)
        String s3 = "a" + "b";              // "a"是常量,"b"是常量,常量+常量=常量,所以会入池
        String s4 = s1 + "b";               // s1是变量,"b"是常量,变量+常量!=常量,所以不会入池
        ------------------------------------------------------------------------------------------------
        字符串常量池是一个固定容量的Map,若容量太小(Number of buckets=60013)、字符串太多(1000 万个字符串)
        那么每一个桶中的字符串数量会非常多,所以搜索起来就很慢
        设置JVM参数(-XX:StringTableSize=10000000),指定更多的桶
        设置JVM参数(-XX:+PrintStringTableStatistic),程序退出时,打印出字符串常量池的统计信息

02.常用结论2
    a.示例1
        a.代码
            int num = 10;
            num = 20;
        b.图示
             栈(基本类型+引用类型)    堆(对象的示例,理解为“new Person()”)     方法区(包含常量池)
            [                    ]      [                    ]             [                       ]
            [                    ]      [                    ]             [  10(×,销毁)          ]
            [         num        ]      [                    ]             [  20(√,改变指向方向)   ]
            [                    ]      [                    ]             [                       ]
            [                    ]      [                    ]             [                       ]
    b.示例2
        a.代码
            String str = 'a';
            str = 'b';
        b.图示
             栈(基本类型+引用类型)    堆(对象的示例,理解为“new Person()”)     方法区(包含常量池)
            [                    ]      [                    ]             [                       ]
            [                    ]      [                    ]             [  a(×,不销毁)         ]
            [         str        ]      [                    ]             [  b(√,改变指向方向)    ]
            [                    ]      [                    ]             [                       ]
            [                    ]      [                    ]             [                       ]
    c.示例3
        a.代码
            String str = 'a';
            str = 'b';
            str += 'c';
        b.图示
           栈(基本类型+引用类型)    堆(对象的示例,理解为“new Person()”)     方法区(包含常量池)
            [                    ]      [                    ]             [                      ]
            [                    ]      [                    ]             [  a(×,不销毁)         ]
            [         str        ]      [                    ]             [  bc(×,不销毁)        ]
            [                    ]      [                    ]             [  bc(√,改变指向方向)  ]
            [                    ]      [                    ]             [                      ]
    d.结论
        字符串是常量,它们的值一旦被创建就无法改变;【常量池中,num会销毁,而str却不会被销毁】
        如果需要对String字符串进行频繁修改,不建议使用String类,推荐使用【StringBuffer类】

03.常用结论3
    a.示例1
        a.代码
            String str1 = "hello";
            String str2 = "hello";
            System.out.println("str1==str2结果为:" + (str1==str2));    // 结果为true
        b.图示:先去“常量池中查找”,若存在,则直接使用;若不存在,则创建后放入常量池,再使用;
             栈(基本类型+引用类型)    堆(对象的示例,理解为“new Person()”)     方法区(包含常量池)
            [                    ]      [                    ]             [                         ]
            [                    ]      [                    ]             [                         ]
            [         str1       ]      [                    ]             [hello(先去“常量池中查找”   ]
            [         str2       ]      [                    ]             [      若存在,则直接使用)  ]
            [                    ]      [                    ]             [                         ]
    b.示例2
        a.代码
            String str1 = "hello";
            String str2 = "hello";
            String str3 = new String("hello");
            String str4 = new String("hello");
            String str5 = new String("hello");
            System.out.println("str1==str2结果为:" + (str1==str2));    // 结果为true
            System.out.println("str3==str4结果为:" + (str3==str4));    // 结果为false
            System.out.println("str1==str4结果为:" + (str1==str4));    // 结果为false
        b.图示:new String("hello"):凡是有NEW关键字,就会在"堆中开辟新的内存空间"
             栈(基本类型+引用类型)    堆(对象的示例,理解为“new Person()”)     方法区(包含常量池)
            [                    ]      [                    ]             [                       ]
            [         str1       ]      [                    ]             [                       ]
            [         str2       ]      [                    ]             [                       ]
            [         str3       ]      [ new String("hello")]             [                       ]
            [         str4       ]      [ new String("hello")]             [hello(若存在,则直接使用)]
            [         str5       ]      [ new String("hello")]             [                       ]
            [                    ]      [                    ]             [                       ]
    c.结论
        使用String字符串时,
        如果直接使用"hello",即String str1 = "hello",则从常量池中寻找;
        如果使用NEW关键字,即String str3 = new String("hello"),则从堆中寻找;
        (而堆中又会从常量池中寻找,如果常量池中不存在,则在常量池创建,并引用常量池中的字符串)
        (引用:使用常量池中的内存地址)
        建议:将频繁使用的常量放入常量池,而非【重复使用NEW关键字开辟内存空间】,以提高空间使用率

04.常用结论4
    a.示例1
        a.代码
            String str1 = "hello";
            String str2 = "hello";
            String str3 = "he" + "llo";
            System.out.println("str1==str2结果为:" + (str1==str2));    // 结果为true
            System.out.println("str1==str3结果为:" + (str1==str3));    // 结果为true
        b.解释
            首先,我们要知道Java会确保一个字符串常量只有一个拷贝。
            因为str1和str2中的”hello”都是字符串常量,它们在编译期就被确定了,所以str1==str2为true;
            而“he”、“llo”也都是字符串常量,【当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量】
            因此在编译期“he”、“llo”就被解析为一个字符串常量,所以,【str3也是常量池中"hello"中的一个引用】
    b.示例2
        a.代码
            String str1 = "hello";
            String str2 = new String("hello");
            String str3 = "he" + new String("llo");
            System.out.println("str1==str2结果为:" + (str1==str2));    // 结果为false
            System.out.println("str1==str3结果为:" + (str1==str3));    // 结果为false
            System.out.println("str2==str3结果为:" + (str2==str3));    // 结果为false
        b.解释
            str1:str1仍使用“常量池中hello的引用”
            str2:【new String()创建字符串不是常量,无法在编译期确定;创建字符串不放入常量池中,有独立地址空间】
            str3:后半部分new String()创建字符串不是常量,无法在编译期确定;因此,也会创建新的"hello"引用

4.3 String常量池

01.字符串常量池
    字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,
    在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,
    如果不存在,则实例化一个字符串放到池中,并返回其引用。

02.字符型常量和字符串常量的区别 
    形式上:字符常量是单引号引起的一个字符,字符串常量是双引号引起的若干个字符
    含义上:字符常量相当于一个整形值(ASCII值),可以参加表达式运算 字符串常量代表一个地址值(该字符串在内存中存放位置)
    占内存大小:字符常量占两个字节,字符串常量占若干个字节(至少一个字符结束标志)

4.4 String.intern()

01.介绍
    a.解读1
        String的intern()就是扩充常量池一个方法
        存在.class文件中常量池,在运行期被JVM装载,并且可以扩充
        当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,
        如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用
    b.解读2
        public native String intern();  // String类被native修饰
        在JDK7后,Oracle接管了JAVA源码后不对外开发,因此,在OpenJDK7源码中,查看String类的intern()方法
        如果常量池中存在当前字符串, 直接返回当前字符串;若没有此字符串, 会将此字符串放入常量池后, 再返回;
        对于任意两个字符串s和t,当且仅当 s.equals(t) 为true时,s.intern()==t.intern() 才为true
        注意一点,【JDK7版本以后,常量池引入到了Heap中,所以可以直接存储引用】
    c.解读3
        字符串如果通过NEW创建,则必然会直接指向堆中的对象;
        如果NEW创建对象后,想把【引用从“堆 -> 常量池”】,则需要调用【String类中的inern()方法】
    d.解读4
        JDK7前,常量池是存放在方法区中
        JDK7,字符串常量池移到了堆中,运行时常量池还在方法区中
        JDK8,删除了永久代,方法区这个概念还是保留的,但是方法区的实现变成了元空间,常量池沿用JDK7放在了堆中
        JDK8,常量池和静态变量存储到了堆中,类的元数据及运行时常量池存储到元空间中
    e.解读5
        Java基本类型的包装类的大部分都实现了常量池技术,即Byte、Short、Integer、Long、Character;
        这5种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象;
        两种浮点数类型的包装类Float、Double,以及Boolean类,并没有实现常量池技术
    f.区分
        字面量:如 “Java”、“Hello World”、100、3.14 等
        常量:相对变量而言,在程序运行其间,不会被程序修改的量
        常量池:运行时常量池、字符串常量池、常量池表的统称,需要根据上下文理解“常量池”代表的具体含义
        常量池表:Class文件中用于存放编译期生成的各种字面量和符号引用,其中String 常量会被放入字符串常量池
        字符串常量池:专门用来存放字符串常量的一个池子,属于“运行时常量池”的一部分
        运行时常量池:方法区的一部分

02.示例1
    a.代码
        String str1 = "hello";
        String str2 = new String("hello");
        String str3 = new String("hello");
        System.out.println(str1 == str2);            // 结果为false
        str2.intern();
        str3 = str3.intern();
        System.out.println(str1 == str2);            // 结果为false
        System.out.println(str1 == str2.intern());   // 结果为true
        System.out.println(str1 == str3);            // 结果为true
    b.解释
        str1==str2结果为:false                       // str2执行intern(),却没有赋给str2
        str1==str2.intern()结果为:true               // str2.intern()确实返回的是常量池中"hello"引用

03.示例2
    a.代码
        String str1 = new String("hello");
        String str2 = str1.intern();
        System.out.println(str1 == str1.intern());   // 结果为false
        System.out.println(str2 == str1.intern());   // 结果为true
    b.解释
        常量池中一开始没有”hello”,当调用str1.intern()后,在常量池中新添加一个“hello”常量;
        原来的不在常量池中的”kvill”仍然存在,也就不是“如果表中没有相同值的字符串,则将自己地址注册到表中”

04.示例3
    a.常量池中存在字符串
        // 在堆中创建对象,在字符串常量池中添加hello,【引用情况:a(hello) -> 堆地址 -> 常量池地址】
        String a = new String("hello");
        // 调用intern,此时hello已存入常量池中,【引用情况:b(hello) -> 常量池地址】
        String b = a.intern();
        // 【引用情况:a(hello) -> 堆地址 -> 常量池地址】,【引用情况:b(hello) -> 常量池地址】
        System.out.println(a == b);                      // 结果为false

        // 因为常量池中已经包含hello,【引用情况:c(hello) -> 常量池地址】
        String c = "hello";
        // 【引用情况:b -> 常量池地址】,【引用情况:c(hello) -> 常量池地址】
        System.out.println(b == c);                      // 结果为true
    b.常量池中不存在字符串
        // 在堆中创建对象,在字符串常量池中添加hello、world,但无法添加helloworld,
        // 【引用情况:a(helloworld) -> 堆地址】
        String a = new String("hello") + "world";
        // 调用intern,此时常量池无"helloworld",则在常量池创建,【引用情况:b(helloworld) -> 常量池地址】
        String b = a.intern();
        // 【引用情况:a(helloworld) -> 堆地址】,【引用情况:b(helloworld) -> 常量池地址】
        // 【JDK7版本以后,常量池引入到Heap中,直接存储引用,故b(helloworld) -> Heap的常量池地址】
        System.out.println(a == b);                      // 结果为true

        // 字符串常量池中已存在"helloworld",【引用情况:c(helloworld) -> 常量池地址】,同a、b引用地址
        String c = "helloworld";
        System.out.println(a == c);                      // 结果为true
        System.out.println(b == c);                      // 结果为true

        // 在堆中创建对象,在字符串常量池中添加he、llo,但无法添加hello,【引用情况:d(hello) -> 堆地址】
        String d = new String("he") + "llo";
        // 由于变量a已经在常量池中添加"hello",【引用情况:e(hello) -> 常量池地址】
        String e = d.intern();
        System.out.println(d == e);                      // 结果为false

4.5 String.split()

01.介绍:正则表达式,分割字符串,但存在性能不稳定的问题(建议:String.indexOf()优先处理字符串的分割)
    a.方法
        public String[] split(String regex)
        public String[] split(String regex, int limit)
    b.参数说明
        a.regex
            正则表达式分隔符
        b.limit
            分割的份数,默认为0
            limit > 0:最多匹配limit-1次,但,结尾空字符串将被丢弃,其他位置不受影响
            limit = 0:尽可能的多匹配,且长度无限制,但结尾空字符串将被丢弃,其他位置不受影响
            limit < 0:尽可能的多匹配,且长度无限制
    c.示例
        a.代码1:limit > 0
            a.代码
                String string = ",a,b,c,d,,";
                String[] strings = string.split(",", 4);
                System.out.println("长度:" + strings.length);
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                长度:4
                ◡
                a
                b
                c,d,,
        b.代码2:limit = 0
            a.代码
                String string = ",a,b,c,d,,";
                String[] strings = string.split(",");
                System.out.println("长度:" + strings.length);
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                长度:5
                ◡
                a
                b
                c
                d
        c.代码3:limit < 0
            a.代码
                String string = ",a,b,c,d,,";
                String[] strings = string.split(",", -1);
                System.out.println("长度:" + strings.length);
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                长度:7
                ◡
                a
                b
                c
                d
                ◡
                ◡

02.常见分割(其他元字符:\d 数字,\D 非数字,\s 空白字符,\S 非空白字符,\w 单词字符,\W 非单词字符)
    a.示例1:元字符 . $ ( ) [ { ^ ? * +
        a.代码1:错误示例
            a.代码
                String string = ".a.b.c.d..";
                String[] strings = string.split(".");
                System.out.println("长度:" + strings.length);
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                长度:0
        b.代码2:正确示例,. $ | *等转义字符,必须得加 \\
            a.代码
                String string = ".a.b.c.d..";
                String[] strings = string.split("\\.");
                System.out.println("长度:" + strings.length);
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                长度:5
                ◡
                a
                b
                c
                d
    b.示例2:元字符 |
        a.代码1:错误示例
            a.代码
                String str = "he.llo";
                String[] strings = str.split("|");
                System.out.println("长度:" + strings.length);
                for (int i = 0; i < strings.length; i++) {
                    System.out.println(strings[i]);
                }
            b.结果
                长度:6
                h
                e
                .
                l
                l
                o
        b.代码2:正确示例
            a.代码
                String string = "a=1 and a=2 or a=3";
                String[] strings = string.split("and|or");
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                a=1
                ◡a=2
                ◡a=3
    c.示例3:元字符 \,定义字符串时,不能使用【"aa\bb\cc"】,需要转义为【"aa\\bb\\cc"】
        a.代码1:错误示例
            a.代码
                String string = "aa\\bb\\cc";
                String[] strings = string.split("\\");
                System.out.println("长度:" + strings.length);
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                报错:Illegal/unsupported escape sequence
        b.代码2:正确示例
            a.代码
                String string = "aa\\bb\\cc";
                String[] strings = string.split("\\\\");
                System.out.println("长度:" + strings.length);
                for (String str : strings) {
                    System.out.println(str);
                }
            b.结果
                长度:3
                aa
                bb
                cc

4.6 String.substring()

01.介绍:指定范围,截取字符串,返回原字符串的一个子字符串
    a.方法
        public String substring(int beginIndex)
        public String substring(int beginIndex, int endIndex)
    b.参数说明
        a.beginxIndex
            起始索引(包括),从0开始,范围:[),长度:endIndex-beginIndex
        b.endIndex
            结束索引(不包括)

02.示例
    a.代码1
        a.代码
            String Str = new String("0123456789");
            System.out.println("截取第1组值:" + Str.substring(4));
            System.out.println("截取第1组长度:" + Str.substring(4).length());
        b.结果
            截取第1组值:456789
            截取第1组长度:6
    b.代码2
        a.代码
            String Str = new String("0123456789");
            System.out.println("截取第2组值:" + Str.substring(4, 8));
            System.out.println("截取第2组长度:" + Str.substring(4, 8).length());
        b.结果
            截取第2组值:4567
            截取第2组长度:4

4.7 StringTokenizer类

01.构造方法:将字符串分解为【单词】,推荐使用StringTokenizer类,比直接使用String.split()方法效率高
    public StringTokenizer(String str) {                // 默认以” \t\n\r\f”(前有一个空格,引号不是)为分割符
        this(str, " \t\n\r\f", false);
    }
    public StringTokenizer(String str, String delim) {  // 指定delim为分割符
        this(str, delim, false);
    }
    public StringTokenizer(String str, String delim, boolean returnDelims) {// 第三个参数true,delim当作标记
        currentPosition = 0;
        newPosition = -1;
        delimsChanged = false;
        this.str = str;
        maxPosition = str.length();
        delimiters = delim;
        retDelims = returnDelims;
        setMaxDelimCodePoint();
    }

02.方法
    int countTokens()           --返回nextToken方法被调用的次数
    boolean hasMoreTokens()     --返回是否还有分隔符
    boolean hasMoreElements()   --返回是否还有分隔符,调用hasMoreTokens方法,实现Enumeration接口方法
    String nextToken()          --返回当前位置到下一个分隔符的字符串
    Object nextElement()        --返回当前位置到下一个分隔符的字符串,调用nextToken方法,实现Enumeration接口

03.常见使用
    a.示例1
        a.代码
            StringTokenizer st = new StringTokenizer("www.ooobj.com", ".b");
            while(st.hasMoreTokens()){
                System.out.println("分割的每个Token为:" + st.nextToken());
            }
        b.结果
            分割的每个Token为:www
            分割的每个Token为:ooo
            分割的每个Token为:j
            分割的每个Token为:com
    b.示例2
        a.代码
            StringTokenizer st = new StringTokenizer("www.ooobj.com", ".b", true);
            while(st.hasMoreTokens()){
                System.out.println("分割的每个Token为:" + st.nextToken());
            }
        b.结果
            分割的每个Token为:www
            分割的每个Token为:.
            分割的每个Token为:ooo
            分割的每个Token为:b
            分割的每个Token为:j
            分割的每个Token为:.
            分割的每个Token为:com

4.8 StringBuilder和+

01.分析【循环内,尽量减少“+”字符串拼接,推荐使用StringBuild、StringBuffer】
    a.示例1
        new关键字,只出现了1次
        009: new           #5   // class java/lang/StringBuilder
    b.示例2
        new关键字,每循环一次就会创建一次StringBuilder对象
        003: new           #3   // class java/lang/StringBuilder
        039: new           #3   // class java/lang/StringBuilder
        063: new           #3   // class java/lang/StringBuilder
        084: new           #3   // class java/lang/StringBuilder
        110: new           #3   // class java/lang/StringBuilder
    c.示例3
        000: new           #2   // class java/lang/StringBuilder

02.示例1
    a.代码:证明StringBuilder使用append()来拼接字符串
        public class Demo01 {
            public static void main(String[] args) {
                String s1 = "Hello";
                String s2 = "World";
                System.out.println(s1 + s2);
            }
        }
    b.结果
        public class org.myslayers.controller.Demo01 {
          public org.myslayers.controller.Demo01();
            Code:
               0: aload_0
               1: invokespecial #1   // Method java/lang/Object."<init>":()V
               4: return

          public static void main(java.lang.String[]);
            Code:
               0: ldc           #2   // String Hello
               2: astore_1
               3: ldc           #3   // String World
               5: astore_2
               6: getstatic     #4   // Field java/lang/System.out:Ljava/io/PrintStream;
               9: new           #5   // class java/lang/StringBuilder
              12: dup
              13: invokespecial #6   // Method java/lang/StringBuilder."<init>":()V
              16: aload_1
              17: invokevirtual #7   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              20: aload_2
              21: invokevirtual #7   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              24: invokevirtual #8   // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              27: invokevirtual #9   // Method java/io/PrintStream.println:(Ljava/lang/String;)V
              30: return
        }

03.示例2
    a.代码:数组拼接成指定格式的字符串
        public class Demo01 {
            public static String arrayToString(String[] arr) {
                String s = "";
                s += "[";
                for (int x = 0; x < arr.length; x++) {
                    if (x == arr.length - 1) {
                        s += arr[x];
                    } else {
                        s += arr[x];
                        s += ", ";
                    }
                }
                s += "]";
                return s;
            }

            public static void main(String[] args) {
                String[] arr = {"Hello", "World", "!!!"};
                String s1 = arrayToString(arr);
                System.out.println(s1);
            }
        }
    b.结果
        public class org.myslayers.controller.Demo01 {
          public org.myslayers.controller.Demo01();
            Code:
               0: aload_0
               1: invokespecial #1   // Method java/lang/Object."<init>":()V
               4: return

          public static java.lang.String arrayToString(java.lang.String[]);
            Code:
               0: ldc           #2   // String
               2: astore_1
               3: new           #3   // class java/lang/StringBuilder
               6: dup
               7: invokespecial #4   // Method java/lang/StringBuilder."<init>":()V
              10: aload_1
              11: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              14: ldc           #6   // String [
              16: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              19: invokevirtual #7   // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              22: astore_1
              23: iconst_0
              24: istore_2
              25: iload_2
              26: aload_0
              27: arraylength
              28: if_icmpge     110
              31: iload_2
              32: aload_0
              33: arraylength
              34: iconst_1
              35: isub
              36: if_icmpne     63
              39: new           #3   // class java/lang/StringBuilder
              42: dup
              43: invokespecial #4   // Method java/lang/StringBuilder."<init>":()V
              46: aload_1
              47: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              50: aload_0
              51: iload_2
              52: aaload
              53: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              56: invokevirtual #7   // Method java/lang/StringBuilder.toString:java/lang/String;
              59: astore_1
              60: goto          104
              63: new           #3   // class java/lang/StringBuilder
              66: dup
              67: invokespecial #4   // Method java/lang/StringBuilder."<init>":()V
              70: aload_1
              71: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              74: aload_0
              75: iload_2
              76: aaload
              77: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              80: invokevirtual #7   // Method java/lang/StringBuilder.toString:java/lang/String;
              83: astore_1
              84: new           #3   // class java/lang/StringBuilder
              87: dup
              88: invokespecial #4   // Method java/lang/StringBuilder."<init>":()V
              91: aload_1
              92: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              95: ldc           #8   // String ,
              97: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
             100: invokevirtual #7   // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
             103: astore_1
             104: iinc          2, 1
             107: goto          25
             110: new           #3   // class java/lang/StringBuilder
             113: dup
             114: invokespecial #4   // Method java/lang/StringBuilder."<init>":()V
             117: aload_1
             118: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
             121: ldc           #9   // String ]
             123: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
             126: invokevirtual #7   // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
             129: astore_1
             130: aload_1
             131: areturn

          public static void main(java.lang.String[]);
            Code:
               0: iconst_2
               1: anewarray     #10   // class java/lang/String
               4: dup
               5: iconst_0
               6: ldc           #11   // String Hello
               8: aastore
               9: dup
              10: iconst_1
              11: ldc           #12   // String World
              13: aastore
              14: astore_1
              15: aload_1
              16: invokestatic  #13   // Method arrayToString:([Ljava/lang/String;)Ljava/lang/String;
              19: astore_2
              20: getstatic     #14   // Field java/lang/System.out:Ljava/io/PrintStream;
              23: aload_2
              24: invokevirtual #15    // Method java/io/PrintStream.println:(Ljava/lang/String;)V
              27: return
        }

04.示例3
    a.代码:在循环中使用StringBuilder拼接字符串
        public class Demo01 {
            public static String arrayToString(String[] arr) {
                StringBuilder s = new StringBuilder();
                s.append("[");
                for (int x = 0; x < arr.length; x++) {
                    if (x == arr.length - 1) {
                        s.append(arr[x]);
                    } else {
                        s.append(arr[x]);
                        s.append(", ");
                    }
                }
                s.append("]");
                return s.toString();
            }


            public static void main(String[] args) {
                String[] arr = {"Hello", "World"};
                String s1 = arrayToString(arr);
                System.out.println(s1);
            }
        }
    b.结果
        public class org.myslayers.controller.Demo01 {
          public org.myslayers.controller.Demo01();
            Code:
               0: aload_0
               1: invokespecial #1   // Method java/lang/Object."<init>":()V
               4: return

          public static java.lang.String arrayToString(java.lang.String[]);
            Code:
               0: new           #2   // class java/lang/StringBuilder
               3: dup
               4: invokespecial #3   // Method java/lang/StringBuilder."<init>":()V
               7: astore_1
               8: aload_1
               9: ldc           #4   // String [
              11: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              14: pop
              15: iconst_0
              16: istore_2
              17: iload_2
              18: aload_0
              19: arraylength
              20: if_icmpge     63
              23: iload_2
              24: aload_0
              25: arraylength
              26: iconst_1
              27: isub
              28: if_icmpne     42
              31: aload_1
              32: aload_0
              33: iload_2
              34: aaload
              35: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              38: pop
              39: goto          57
              42: aload_1
              43: aload_0
              44: iload_2
              45: aaload
              46: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              49: pop
              50: aload_1
              51: ldc           #6   // String ,
              53: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              56: pop
              57: iinc          2, 1
              60: goto          17
              63: aload_1
              64: ldc           #7   // String ]
              66: invokevirtual #5   // Method java/lang/StringBuilder.append:java/lang/StringBuilder;
              69: pop
              70: aload_1
              71: invokevirtual #8   // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              74: areturn

          public static void main(java.lang.String[]);
            Code:
               0: iconst_2
               1: anewarray     #9   // class java/lang/String
               4: dup
               5: iconst_0
               6: ldc           #10  // String Hello
               8: aastore
               9: dup
              10: iconst_1
              11: ldc           #11  // String World
              13: aastore
              14: astore_1
              15: aload_1
              16: invokestatic  #12  // Method arrayToString:([Ljava/lang/String;)Ljava/lang/String;
              19: astore_2
              20: getstatic     #13  // Field java/lang/System.out:Ljava/io/PrintStream;
              23: aload_2
              24: invokevirtual #14  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
              27: return
        }