第三章 Java的基本程序设计结构

梦的开始--一个简单的Java应用程序

  • 直接上代码

     /**
      * @Author : zxl
      * @create 2022/10/25 16:49
      */
     public class FirstSimple {
         public static void main(String[] args) {
             System.out.println("we will never user hello world");
         }
     }
  • 解析

    • 这里使用了 System.out 对象并调用了它的 println 方法。 点号( • )用于调用方法。Java 函数调用语法是 object,method(parameters)

      这个示例调用了 println 方法并传递给它一个字符串参数,作用是将传递给它的字符串参数显示在控制台上。

  • 易错点

    • 大小写拼写错误,例如将main 写成 Main,而Java严格区分大小写,程序将无法执行

    • 关键字public是访问修饰符,用于控制程序的其他部分对这段代码的访问级別,后面章节会详细写到

    • 关键字 class 表明 当前文件是一个类,后面紧跟类名,可以将类作为一个容器,用来加载程序逻辑(定义了程序的行为)

    • 标准的命名规范:(类名 FirstSample 就遵循了这个规范):以大写字母开头,如果名字由多个单词组成,每个单词的第一个字母都应该大写,这种方式称为大驼峰命名法。

  • 注意点

    • 在编译这段源代码之后就会得到一个包含这个类的字节码的文件。Java 编译器将字节码文件自动地命名为

      FirstSample. class, 并与源文件存储在同一个目录下。

    • 最后, 使用这行命令运行这个程序:java FirstSample,Java 虚拟机将从类中的 main 方法开始执行,因此为了代码能够执行,在类的源文件中必须包含一个 main方法。当然,也可以将用户自定义的方法添加到类中,并且在 main 方法中调用它们(第 4 章会讲到)

    • 大括号代表方法的开始和结束,而大括号风格看个人,由于空白符会被 Java 编译器忽略,所以可以选用自己喜欢的大括号风格

    • main 方法没有为操作系统返回“ 退出代码” , main 方法运行接受后的退出代码默认为 0,表示成功地运行了程序。如果希望返回其他的代码, 需要调用 System.exit() 方法

阴阳双生-代码的另外一面--注释

  • Java 中的注释不会出现在可执行程序中。可以在源程序中根据需要添加任意多的注释,而不必担心可执行代码会膨胀

  • Java有三种注释:

    • // 单行注释

    • /**/ 支持多行注释

    • /** + 回车 生成文档注释

  • 警告:

    • 在 Java 中,/* / 注释不能嵌套 „ 也就是说, 不能简单地把代码用 / 和 */ 括起来作为注释, 因为这段代码本身可能也包含一个 */ ,

世界本源--数据类型

Java是一种强类型语言,就是说每一个变量是必须声明类型的,一共有八种基本类型(4+2+1+1),分别有4种整型、2种浮点类型、1种字符类型、1种表示真值的boolean类型,即4+2+1+1

  • 整型

    • 整型表示没有小数部分的数值,允许负数,四种如以下所示

      类型存储需求取值范围
      byte1字节-128~127
      short2字节-32768~32767
      int4字节-2147483648~2147483647(超过20亿)
      long8字节-(2的63次方+1) ~ 2的63次方

    • 使用场景

      • int最为常用,如果想表示全地球人口,此时可以用long,如果是存储空间很宝贵的大数组,此时可以用byte或者short

    • 平台移植问题

      • C和C++程序会针对不同处理器选择最为高效的整型,这样会造成一个在32位处理器运行正常的程序在16处理器却发生了整数溢出

      • Java为了保障在所有机器上都能得到相同的结果,所以各种数据类型的取值范围都保持固定,这样不同平台或者不同操作系统的代码移植问题就有效解决了

    • 前后缀:

      • 长整型后缀L或l(如5000000000L)

      • 十六进制前缀0x或0X(如0xBCFE),八进制前缀为o。例如010对应十进制的8,因比较容易混淆故不推荐使用

      • Java7开始,加上前缀0b或0B可以表示二进制,例如ob101表示5,并可以为数字字面量添加下划线,例如1_000_000,与1000000没有区别,目的是为让人更容易读,编译器会去除这些下划线


  • 浮点类型

    • 浮点类型用于表示有小数部分的数值,Java有两种浮点类型,如下所示:

      类型存储需求取值范围
      float4字节大约 +-3.40282347E+38F(有效位数6~7位)
      double8字节大约 +-1.79769313486231570E+308(有效位数15位)

    • double的数值精度(即取值范围)是float类型的两倍,所以称之为双精度数值,使用float时会有后缀f或F,不加默认为double类型,绝大部分的应用程序都会采用double,只有极少数适合使用float,例如,需要单精度数据的库时

    • 表示溢出和出错情况的三个特殊浮点数值

      • 正无穷大

      • 负无穷大

      • NaN(不是一个数字)

    • 正整数除以0的结果为正无穷大, 计算0/0或者负数平方根结果是NaN

               // 1.0 / 0.0
               double positiveInfinity = Double.POSITIVE_INFINITY;
               System.out.println("positiveInfinity = " + positiveInfinity);
               // 输出: positiveInfinity = Infinity
       ​
               // -1.0 / 0.0
               double negativeInfinity = Double.NEGATIVE_INFINITY;
               System.out.println("negativeInfinity = " + negativeInfinity);
               //输出 negativeInfinity = -Infinity
       ​
               // 0.0d / 0.0
               double naN = Double.NaN;
               System.out.println("naN = " + naN);
               // 输出 naN = NaN

    • 特别说明:NaN的使用事项

               // 两个NaN是永远不会相等的
               if (Double.NaN == Double.NaN) {
                   // 永远不会输出
                   System.out.println("两个NaN会相等");
               }
               //可以使用 Double.isNaN 方法
               double x = 0.0d / 0.0;
               if (Double.isNaN(x)) {
                   // 会输出 x 是一个 NaN
                   System.out.println("x 是一个 NaN");
               }

    • 警告:浮点数值不适用于无法接受四舍五入的金融计算,浮点数采用二进制系统表示,无法精确表示1/10,就像十进制无法表示1/3一样

                       System.out.println((2.0-1.1));
               // 输出结果: 0.8999999999999999

  • char类型

    • 字符表示

      • char类型字面量要用单引号‘’括起来,例如 ‘A’表示编码值为65的字符常量,与 “A”不同,“A”是包含字符A的字符串

      • 一个char类型可以表示单个字符,也可以表示一个Unicode字符,有些Unicode字符需要两个char来表示

    • 转义序列

      转义序列名称Unicode值
      \b退格\u0008
      \t制表\u0009
      \n换行\u000a
      \r回车\u000d
      \“双引号\u0022
      \‘单引号\0027
      \\反斜杠\u005c
      • char值可以表示为十六进制值,范围是 \u0000~\uffff

      • Unicode转义序列会在解析代码之前得到处理,例如“ \u0022 + \u0022 ”,此时\u0022会优先解析为双引号",结果是 " " + " "得到一个空串

      • 还有就是像这种 // \uooao 这种注释会有语法错误,因为读程序时它会替换为一个换行符, 还有 // \user这种注释,由于\u后面没有带着四个十六进制位,也会报错


  • boolean类型

    • boolean (布尔)类型有两个值:false 和 true,要注意的是整型值和布尔值之间

      不能进行相互转换


异空间-变量的世界

  • 前言

    • 变量声明: 声明是一条完整的 Java语句,必须以分号结束,例如 int x; double y;

    • 变量取名规范:必须由字母开头,由字母和数字组合而且,当然也可以没有数字

      • 字母不仅仅有 ’A’ ~ ’Z’、 ’a’~’z’, 下划线“_”和美元“$”符号都是字母,并且其它国家能代表字母的unicode字符,都可以用来取名,另外,不能使用 Java 保留字作为变量名

      • ‘+’和空格或者其它特殊字符不行,而且不建议使用“$”取名,它一般只用在 Java 编译器或其他工具生成的名字中。

    • 提示:如果想要知道哪些 Unicode 字符属于 Java 中的字母, 可以使用 Character 类的

      isJavaldentifierStart 或 isJavaldentifierPart 方法来检查。

       public class FirstSimple {
           public static void main(String[] args) {
               boolean b = Character.isJavaIdentifierStart('$');
               System.out.println("b = " + b);
               // 输出 b = true
             
             boolean b = Character.isJavaIdentifierPart('+');
               System.out.println("b = " + b);
               // 输出 b = false
           }
       }

  • 变量初始化

    • 基本数据类型是必须先初始化的,没有默认值

    • 我们一般将变量的声明和初始化放在同一行

       public class FirstSimple {
           public static void main(String[] args) {
                   int a = 12;
                   
       //      当然也可以分开
               int b;
               b = 12;
           }
       }
    • 变量的声明尽可能挨近第一次使用变量的地方,这是一种良好的编程习惯

  • 常量

    • 用关键字final来表示常量, 该常量只能被赋值一次。一旦被赋值之后,就不能够再更改了,通常常量名使用全大写。

       /**
        * @Author : zxl
        * @create 2022/10/26 15:24
        */
       public class Constants {
           public static void main(String[] args) {
               final double CONSTANTS = 2.54;
               System.out.println("CONSTANTS = " + CONSTANTS); // CONSTANTS = 2.54
           }
       }

    • Java 中,如果希望某个常量可以在一个类中的多个方法中使用,可以使用关键字 static fina设置一个类常量,因为静态和非静态都可以访问静态属性,而静态方法不可以访问非静态属性,

       public class Constants2 {
           // 定义在main方法外面 如果希望在其他类也可以访问该常量时 可以加上public修饰符
           public static final double CONSTANTS = 2.54;
           public static void main(String[] args) {
               System.out.println("CONSTANTS = " + CONSTANTS); // CONSTANTS = 2.54
           }
       }
       ​
       import static com.zxl.Constants2.CONSTANTS;
       public class Constants {
           public static void main(String[] args) {
             // 我在本类访问到了Constants2这个类的常量
               System.out.println("CONSTANTS = " + CONSTANTS); // CONSTANTS = 2.54
           }
       }


十八般武艺-运算符

  • 前言

    • Java 中,使用算术运算符 +、-、 *、/ 表示加、减、 乘、除运算。

    • 当参与 / 运算的两个操作数都是整数时, 表示整数除法;否则, 表示浮点除法。 整数的求余操作(有时称为取模) 用 % 表示。例如,15/2 等于 7 ,15%2 等于 1 , 15.0/2 等于 7.50

    • 注意: 整数被 0 除将会产生一个异常, 而浮点数被 0 除将会得到无穷大或 NaN 结果。

  • 数学函数与常量

    • Java提供了一个Math类,该类方法是静态的,包括了很多数学函数以及π和E的近似值

      // 这里我们直接import导入  就不能用写类似 Math.sqrt() 的写法了
      import static java.lang.Math.*;
      
      public class Test {
          public static void main(String[] args) {
              // 4的平方根 
              System.out.println(sqrt(4));// 2.0
      
              // 2的3次方
              System.out.println(pow(2,3));// 8.0
      
              // 12 对10求余
              System.out.println(floorMod(12,10));// 2
            
              // π和E的近似值,小数点到19位
              // PI:3.141592653589793 E:2.718281828459045
              System.out.println("PI:" + PI + " E:" + E);
            
              // E的2次幂
              System.out.println(exp(2)); // 7.38905609893065
              System.out.println(pow(E,2));// 7.3890560989306495
          }
      }

    • 如果得到一个完全可预测的结果比运行速度更重要的话, 那么就应该使用 StrictMath,以确保在所有平台上得到相同的结果,使用方法和Math几乎相同

  • 数值类型之间的转换

    • 两个数值进行二元操作时,先操作数转换为同一种类型,然后再进行计算。

      • 如果两个操作数中有一个是 double 类型, 另一个操作数就会转换为 double 类型。

      • 否则,如果其中一个操作数是 float 类型,另一个操作数将会转换为 float 类型。

      • 否则, 如果其中一个操作数是 long 类型, 另一个操作数将会转换为 long 类型。

      • 否则, 两个操作数都将被转换为 int 类型。

      • int 转 float会丢失精度, int 允许自动转换到double类型, long转float和double都会丢失精度

  • 强制类型转换

    • 在 Java 中, 允许精度丢失的类型转换。需要通过强制类型转换实现这个操作,如下所示

       public class Test {
           public static void main(String[] args) {
               double d = 4.1;
               int i = (int) d;
               System.out.println("i = " + i);// i = 4
           }
       }

  • 结合赋值和运算符

    • 主要讲一下+=运算符

    • 此时定义一个变量 int x = 0; 例如 x+=4; 等价于 x= x+4;

    • 如果运算后值类型与左侧操作数类型不一致,则会发生强制转换,例如 x+=2.2; 等价于x = (int)x+2.2;

  • 自增与自减

    • 这里简单说一下就好了,直接上代码

       public class Test {
           public static void main(String[] args) {
               int a = 1;
               int b = 1;
       ​
               //前缀++  a 先加1后进行操作
               int c = 1 + ++a;  // 1+2
               System.out.println("c = " + c);//c = 3
       ​
               //后缀++  b 先操作后加1
               int d = 1 + b++;  // 1+1
               System.out.println("d = " + d);//d = 2
           }
       }
    • 操作数不能是数值。例如, 1++ 就不是一个合法的语句

    • 建议不要在表达式中使用 ++, 因为这样的代码很容易让人困惑,而且会带来烦人的 bug。

  • 关系和boolean运算符

    • == 用来检测相等性 !=表示不相等 返回true或false

    • 还有老生常谈的< (小于、) > (大于)、<=(小于等于)和 >= (大于等于)运算符

    • 使用 && 表示逻辑“ 与” 运算符,使用|| 表示逻辑“ 或” 运算符,感叹号!就是逻辑非运算符。

    • && 和|| 运算符是按照“ 短路” 方式来求值的

      • 使用&&逻辑与时 例如 x>1 && y<2 如果已经计算得到第一个表达式的值为 false,第二个就不用计算了, 那么结果就不可能为 true

      • 使用||逻辑或时 例如 x>1 || y<2 如果已经计算得到第一个表达式的值为 true, 第二个表达式也不用计算了, 那么结果就不可能为 false

    • 可以利用“短路”这一点来避免错误,例如 x != 0 && 4/x==0 ,如果x为0,就不会计算4/x了,也就不会出现除数为0的情况

    • 最后,还有三元操作符 ?:,如果条件为 true, 下面的表达式

      {x < y ? x : y} 会返回 x 和 y 中较小的一个,如果第一个表达式 x<y 成立,就为返回x的值,否则返回y的值。

  • 位运算符

    • 包括四种: & ("and") | ("or") A ("XOr") ~ ("not")

    • 利用 & 并结合使用适当的 2 的幂, 可以把其他位掩掉, 而只保留其中的某一位

      public class Test {
          public static void main(String[] args) {
              // 8 的 二进制为: 1000
              int fourthBit = (8 & 0b1000) / 0b1000;
              System.out.println("fourthBit = " + fourthBit); // fourthBit = 1
          }
      }
    • 需要注意的是, & |并不具备短路功能

    • 另外,还有>>和 << 运算符将位模式左移或右移。

      public class Test {
          public static void main(String[] args) {
              int num = 1;
              int i = num << 2;
              // 1 * 2的 n 次幂  此时n就是左移数2
              System.out.println("i 左移2位后 " + i);// i 左移2位后 4
      
              // 4 * 2的 n 次幂  此时n就是右移数1
              int i1 = i >> 1;
              System.out.println("i1 右移一位后 " + i1);// i1 右移一位后 2
          }
      }
    • 最后,无符号右移>>> 运算符会用 0 填充高位,这与>>不同,它会用符号位填充高位。不存在无符号左移<<<运算符

  • 括号与运算符级别

    • 如果不使用圆括号, 就按照规定的运算符优先级次进行计算。同一个级别的运算符按照从左到右的次序进行计算(右结合运算符除外)

    • 例如,由于 && 的优先级比 || 的优先级高, 所以表达式

      a && b || c 等价于 (a && b) || c

      又因为 += 是右结合运算符, 所以表达式

      a += b += c 等价于 a += (b += c)

      也就是将 b += c 的结果(加上 c 之后的 b) 加到 a 上。

  • 枚举类型:有时候,变量的取值只在一个有限的集合内。在变量中很可能保存的是一个错误的值,针对这种情况,可以自定义枚举类型,在第五章会详细介绍到


字符串

  • 前言:

    • Java 没有内置的字符串类型, 而是在标准 Java 类库中提供了一个预定义类,很自然地叫做 String。每个用双引号括起来的字符串都是 String类的一个实例: String str = "hello"

  • 子串

    • String类的substring()方法可以从一个较大字符串提取一个较小的子串,例如

               String str = "i am java man";
               String substring = str.substring(5, 9);
               System.out.println("substring = " + substring); //substring = java
    • str.substring(5,9):第一个参数是首先截取的索引位置,第二个参数是第一个不想截取的索引位置,代表从索引为5开始,索引为8结束,即(5,8]

  • 拼接

    • 使用 + 号连接(拼接)两个字符串

      • 当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串,例如 "age" + 18, 结果是 “age18”

    • 使用静态 join 方法

      • 把多个字符串放在一起, 用一个定界符分隔,

         String join = String.join("~", "A", "B", "C");
         System.out.println("join = " + join);// join = A~B~C

  • 不可变字符串

    • 由于不能修改字符串中的字符, 所以在 Java 文档中将 String 类对象称为不可变字符串, 此时我们只能通过截取和拼接来实现“hello”到“help!”的 修改,实际上是创建了一个新的对象

       public class Test {
           public static void main(String[] args) {
               String hl = "hello";
               String hp = hl.substring(0, 3) + "p!";
               System.out.println("hp = " + hp);//hp = hp!
           }
       }
    • 不过我们可以修改字符串变量 h1, 让它引用另外一个字符串, 这就如同可以将存放 1 的数值变量改成存放 2 一样

    • 通过拼接“ hel ” 和“ p!” 来创建一个新字符串的效率确实不高。但是,不可变字符串却有一个优点:编译器可以让字符串共享,可以想象将各种字符串存放在公共的存储池中,字符串变量指向存储池中相应的位置

    • 总之,Java 的设计者认为共享带来的高效率远远胜过于截取、 拼接字符串所带来的低效率

  • 检测字符是否相等

    • 使用 equals 方法检测两个字符串是否相等, 相等返回 true ; 否则返回 false。

    • 一定不要使用==运算符检测两个字符串是否相等! 因为它比较的是值,而字符串变量存储的是存储池中字符串的地址,但Java虚拟机并不总是使两个相同的字符串共享一个位置,完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上,除非使用的是字面量进行比较

       /**
        * @Author zxl
        * @Created 2022/10/27 10:09 
        */
       public class Test {
           public static void main(String[] args) {
               String javaMan = "i am java man";
             // 通过字面量赋值的比较
               if (javaMan == "i am java man") {
                   System.out.println(true);// true
               }
             // 字面量与非字面量的比较
               if (javaMan.substring(5, 9) == "java") {
               } else {
                   System.out.println(false);//false
               }
           }
       }
       ​
    • 总而言之,如果虚拟机始终将相同的字符串共享, 就可以使用= =运算符检测是否相等。这是没毛病的,但实际上只有字符串常量是共享的,而 + 号 或 substring方法 等操作产生的字符串并不是共享的。因此,千万不要使甩= =运算符来判断是否相等

  • 空串与null串

    • 空串是长度为0的字符串,它是一个 Java 对象,有自己的串长度(0) 和内容(空),用以下两种方式判断

        javaMan.length() == 0;
        "".equals(javaMan);
    • String变量还可以存null,表示没有任何对象与其有关联

       /**
        * @Author zxl
        * @Created 2022/10/27 10:09
        */
       public class Test {
           public static void main(String[] args) {
               String javaMan = "i am javaMan";
               if (javaMan == null) {
                   System.out.println("javaMan is " + null);
               } else {
                   System.out.println("no null");
               }
       ​
               // 判断非空非null时,首先要检查 str 不为 null。, 否则为null时 调用length()会空指针异常
               if (javaMan != null && javaMan.length() != 0) {
                   System.out.println("javaMan 非空非null");
               }
       ​
           }
       }

  • 码点与代码单元

    • char 数据类型就是表示 Unicode 码点的代码单元。一般一个这样的代码单元对应一个Unicode字符,而辅助字符需要一对代码单元表示

    • 码点:就是实际看到的每一个字符,比如1、a、@等都算作一个码点

    • 代码单元有可能实际看到的字符是包含一个代码单元,也有可能包含两个代码单元

       /**
        * @Author zxl
        * @Created 2022/10/27 10:09
        */
       public class Test {
           public static void main(String[] args) {
               String javaMan = "i am javaMan";
               //假设这里i字符有两个代码单元表示 这里lenth就是14个了    
               System.out.println("javaMan.length() = " + javaMan.length());
       ​
             // 有多少个字符,就有多少个码点
               System.out.println(javaMan.codePointCount(0, javaMan.length())); //13
           }
       }

  • String API

    • 前言: 在String类中有50种以上的方法,也就是API了,即应用编程接口

       public class Test {
           public static void main(String[] args) {
               String javaMan = "i am javaMan";
       ​
               char charAt = javaMan.charAt(0);
               System.out.println("charAt = " + charAt);//charAt = i
       ​
               int pointAt = javaMan.codePointAt(0);
               System.out.println("pointAt = " + pointAt);//"i"的ASCII码为105 pointAt = 105
       ​
               int length = javaMan.length();
               // 代码单元的个数, 有可能比字符数多,因为一个字符可能有两个代码单元组成
               System.out.println("length = " + length);// length = 12
       ​
               int pointCount = javaMan.codePointCount(0, length);
               System.out.println("pointCount = " + pointCount);// pointCount = 12
       ​
               int codePoints = javaMan.offsetByCodePoints(2, 5);
               System.out.println("codePoints = " + codePoints);//codePoints = 7 其实就是 2+5, 7为索引
       ​
               int compare = javaMan.compareTo("i am javaMan");
               System.out.println("compare = " + compare);// compare = 0
       ​
               int[] ints = javaMan.codePoints().toArray();
               System.out.println("i = " + ints[0]);// i = 105 这里是ascii码
       ​
       ​
               String newJavaMan = new String(ints, 2, 2);
               System.out.println("newJavaMan = " + newJavaMan);// newJavaMan = am
       ​
               boolean equals = javaMan.equals(newJavaMan);
               System.out.println("equals = " + equals);// equals = false
       ​
               boolean ignoreCase = javaMan.equalsIgnoreCase("I am Javaman");
               System.out.println("ignoreCase = " + ignoreCase);// ignoreCase = true
       ​
               boolean starts = javaMan.startsWith("i");
               System.out.println("starts = " + starts);//starts = true
       ​
               boolean endsWith = javaMan.endsWith("Man");
               System.out.println("endsWith = " + endsWith);// endsWith = true
       ​
               int indexOf = javaMan.indexOf('j');
               System.out.println("indexOf('j') = " + indexOf);//indexOf('j') = 5  返回索引值
               indexOf = javaMan.indexOf(97);// 传入ascii码值
               System.out.println("indexOf(97) = " + indexOf);//indexOf(0) = 2 返回a的首个位置索引
               indexOf = javaMan.indexOf("a", 5);// 从索引5后面找 a 字符的首个索引
               System.out.println("indexOf(a, 5) = " + indexOf);// indexOf(a, 5) = 6
               indexOf = javaMan.indexOf("j", 4);
               System.out.println("indexOf(j, 4) = " + indexOf);// indexOf(j, 4) = 5
       ​
               int lastIndexOf = javaMan.lastIndexOf("a");
               System.out.println("lastIndexOf = " + lastIndexOf);// lastIndexOf = 10 最后一个a的索引
       ​
               String replace = javaMan.replace("java", "c++");
               System.out.println("replace = " + replace);// replace = i am c++Man
       ​
               String substring = javaMan.substring(5);
               System.out.println("substring = " + substring);// substring = javaMan
       ​
               String lowerCase = javaMan.toLowerCase();
               System.out.println("lowerCase = " + lowerCase);// lowerCase = i am javaman
       ​
               String upperCase = javaMan.toUpperCase();
               System.out.println("upperCase = " + upperCase);// upperCase = I AM JAVAMAN
       ​
               String trim = " java ".trim();// 删除头尾空格
               System.out.println("trim = " + trim);// trim = java
       ​
               String join = "".join("/", "acb", javaMan);
               System.out.println("join = " + join);// join = acb/i am javaMan
           }
       }
    • API 注释中, 有一些 CharSequence 类型的参数这是一种接口类型, 所有字符串都属于这个接口,只要看到一个 CharSequence 形参, 完全可以传入 String 类型的实参

  • 构建字符串

    • 有时候需要由较短的字符串构建字符串, 例如来自按键的单词。采用字符串连接的方式效率比较低。每次连接字符串, 都会构建一个新的 String 对象,既耗时, 又浪费空间。但是使用 StringBuilder类就可以很好解决。

       /**
        * @Author : zxl
        * @create 2022/10/27 13:54
        */
       public class Test {
           public static void main(String[] args) {
               // 首先,构建一个空的字符串构建器
               StringBuilder builder = new StringBuilder();
       ​
               // 每当需要添加新内容时,调用append()方法
               builder.append("i ");
               builder.append("love ");
               builder.append("Java");
       ​
               // 调用toString(), 可以构建字符串
               String str = builder.toString();
               System.out.println("str = " + str);//str = i love Java
           }
       }
    • 前身

      • 在 JDK5.0 中引入 StringBuilder 类。 这个类的前身是 StringBuffer, 它支持多线程操作,但是通常都是在单线程中操作字符串的,一般会使用StringBuilder 替代它,两个类的API都是相同的

    • StringBuilder的API中一些重要方法

       /**
        * @Author : zxl
        * @create 2022/10/27 13:54
        */
       public class Test {
           public static void main(String[] args) {
               // 首先,构建一个空的字符串构建器
               StringBuilder builder = new StringBuilder();
       ​
               // 追加新字符串并返回到this对象
               builder.append("i ");
               builder.append("love ");
               builder.append("Java ");
       ​
               // 调用toString(), 可以构建字符串
               String str = builder.toString();
               System.out.println("str = " + str);//str = i love Java
       ​
               // 返回构建器或缓冲器中的代码单元数量。(可能会比字符数多)
               int length = builder.length();
               System.out.println("length = " + length);
       ​
               // 追加一个代码单元  此时一个代码单元就是一个字符(码点)
               // 所以也可以说追加了一个码点
               builder.append('A');
               System.out.println("builder.toString() = " + builder.toString());
       ​
               //如果想追加的 是由两个代码单元组成的码点,调用此方法,可以并将其转换两个代码单元并返回 this。
               builder.appendCodePoint(57837);
               System.out.println("builder.toString() = " + builder.toString());
       ​
               builder.setCharAt(builder.length() - 1, 'B');
               System.out.println("builder.toString() = " + builder.toString());
       ​
               // 在索引为1位置 插入字符串
               builder.insert(1, " ill forever");
               System.out.println("builder.toString() = " + builder.toString());
       ​
               // 在指定位置插入代码单元 
               builder.insert(2, 'w');
               System.out.println("builder.toString() = " + builder.toString());
       ​
               // 删除 开始索引到结束索引-1  即左开右闭
               builder.delete(builder.length() - 3, builder.length());
               System.out.println("builder.toString() = " + builder.toString());
               
           }
       }


输入输出

  • 读取输入

    • 标准输入流的使用:

      • 首先需要构造一个 Scanner 对象,并与“ 标准输入流” System.in 关联

      • Scanner sc = new Scanner(System.in);

      • 现在,就可以使用sc对象的各种方法实现输入操作了。

         /**
          * @Author : zxl
          * @create 2022/10/27 13:54
          */
         public class Test {
             public static void main(String[] args) {
                 Scanner sc = new Scanner(System.in);
         ​
                 System.out.println("请输入你的名字:");
                 //a aa aaa
                 //b bb bbb
                 String name1 = sc.nextLine();
                 String name2 = sc.nextLine();
                 System.out.println("name1 = " + name1);//name2 = a aa aaa
                 //说明空格符会被录入到name1中
                 System.out.println("name2 = " + name2);//name2 = b bb bbb
         ​
                 System.out.println("请输入你的名字:");
                 // a aa
                 String next1 = sc.next();
                 String next2 = sc.next();
                 System.out.println("next = " + next1);// next1 = a
                 //说明输入空格就结束next的录入了
                 System.out.println("next2 = " + next2);// next2 = aa
               
               // 还有一些nextInt nextDouble的,和next用法都一样
             }
         }

  • 格式化输出

    • 注意:可以使用 s 转换符格式化任意的对象, 对于任意实现了 Formattable 接口的对象都将调用 formatTo 方法;否则将调用 toString 方法, 它可以将对象转换为字符串

       /**
        * @Author : zxl
        * @create 2022/10/27 13:54
        */
       public class Test {
           public static void main(String[] args) {
               // 直接打印时,将以 x 对应的数据类型所允许的最大数字位数打印输出 X。
               double dNmu = 1_000.0 / 3.0;
               System.out.println("dNmu = " + dNmu);
       ​
               // 注意, 只有printf方法才可以格式化, 而不是print或println
               // 这里用 8 个字符的宽度(包括小数点)和小数点后两个字符的精度打印 x
               System.out.printf("%8.2f", dNmu);
               // 常用的还有--> s 表示字符串,d 表示十进制整数
             
                //还可以给出控制格式化输出的各种标志  0标志数字面前补o (标志将负数括在括号内  ,添加分组分隔符 12.2就是宽度和精度了
               System.out.printf("%0,(12.2f", dNmu);// (003,333.33)
       ​
               //可以使用静态的 String.format 方法创建一个格式化的字符串
               String message = String.format("你好, %s. 到了明年你就%d岁了", "Java小白", 18);
               System.out.println("message = " + message);//你好, Java小白. 到了明年你就18岁了
       ​
               //日期与时间的格式化--格式包括两个字母,以t开始,以的转换符字母结束(c代表完整日期时间,可自行网上查询)
               System.out.printf("%tc", new Date());// 星期四 十月 27 16:22:50 CST 2022
       ​
               //如果需要多次对日期操作才能实现对每一部分进行格式化就太笨拙了
               //可以指出要被格式化的参数索引,索引必须紧跟在 % 后面,并以 $ 终止,而且,参数索引值从1开始,不是0
               System.out.println("===== 索引的日期打印 ==========");
               System.out.printf("%1$s %2$tB, %2$te, %2$tY", "Due date:", new Date());// Due date: 十月, 27, 2022  YBe对应年月日
       ​
               // 不加索引的方式(去掉索引和$)  < 表示再次使用前面的参数, 也就是e使用B的,Y使用e的,他们是一组的
               System.out.println("===== 不加索引的日期打印 ==========");
               System.out.printf("%s %tB, %<te, %<tY", "Due date:", new Date());// Due date: 十月, 27, 2022
       ​
           }
       }
    • 语法图

  • 文件输入、输出

     /**
      * @Author : zxl
      * @create 2022/10/27 13:54
      */
     public class Test {
         public static void main(String[] args) throws IOException {
             try {
                 //要想对文件进行读取, 首先需要用 File 对象构造一个 Scanner 对象
                 // 在这里指定了 UTF-8 字符编码,如果省略字符编码,会使用运行这个 Java 程序的机器的“ 默认编码”
                 //如果在不同的机器上运行这个程序,可能会有不同的表现,所以最好是指定一下
                 Scanner scanner = new Scanner(new File("C:\\Users\\legendshop202203\\ceshi.txt"), "UTF-8");
                 while (scanner.hasNext()) {
                     System.out.println("scanner.next() = " + scanner.next());
                 }
     ​
             } catch (FileNotFoundException e) {
                 e.printStackTrace();
             }
     ​
             // 构造一个带有字符串参数的 Scanner, 会将字符串解释为数据,而不是文件名
             Scanner scanner = new Scanner("ceshi.txt");
             while (scanner.hasNext()) {
                 System.out.println("scanner.next() = " + scanner.next());//scanner.next() = ceshi.txt
             }
     ​
             // 写入文件需要构造一个 pwWriter对象,只需要提供文件名,和上面创建Scanner时一样, 需要处理异常,
             // 这是因为如果用一个不存在的文件构造一个 Scanner, 或者用一个不能被创建的文件名构造一个 pwWriter,那么就会发生异常。Java 编译器认为这些异常比“ 被零除” 异常更严重
             try {
                 PrintWriter pw = new PrintWriter("ceshi.txt", "UTF-8");
                 
                 pw.write("哈哈");
                 pw.write("哈啊哈");
                 pw.write("嘿嘿");
                 
                 pw.println("哈哈");
                 pw.println("哈啊哈");
                 pw.println("嘿嘿");
     ​
                 //创建一个新的对象,此对对象具有自动刷新的功能,即在释放资源之前就写入数据
                 PrintWriter out = new PrintWriter(new FileWriter("c.txt"), true);
     ​
                 out.write("哈哈");
                 out.write("哈啊哈");
                 out.write("嘿嘿");
                 //输出数据
                 out.print("哈哈");
                 out.print("哈啊哈");
                 out.print("嘿嘿");
     ​
                 //释放资源
                 pw.close();
                 out.close();
     ​
             } catch (FileNotFoundException e) {
                 e.printStackTrace();
             }
     ​
         }
     }     

控制流程

  • 与任何程序设计语言一样, Java 使用条件语句和循环结构确定控制流程

  • 块作用域

    • 由大括号括起来的若干java语句,称为块,即复合语句

    • 块可以嵌套块:

       public class Test {
           public static void main(String[] args) {
               int b;
               {
                   int a;
                 // 但是两个块不能定义同名变量  int a;(error)
               }
           }
       }
    • 在 C++ 中, 可以在嵌套的块中重定义一个变量。在内层定义的变量会覆盖在外层定义的变量。这样,有可能会导致程序设计错误, 因此在 Java 中不允许这样做

  • 条件语句

    • Java 常常希望在某个条件为真时执行多条语句,

    • 使用块 ( 有时称为复合语句)可以在 Java 程序结构中原本只能放置一条 ( 简单)语句的地方放置多条语句

       /**
        * @Author : zxl
        * @create 2022/10/27 13:54
        */
       public class Test {
           public static void main(String[] args) {
               if (2 > 1) {
                   System.out.println(1);
                   System.out.println(2);
                   System.out.println(3);
               } else if (3 > 4) {
                   System.out.println(4);
                   System.out.println(5);
               } else {
                   System.out.println(6);
               } 
           }
       }

  • 循环

    • while的使用

      • 模板

         while(循环条件){
             语句;
         }
      • 例如

         /**
          * @Author : zxl
          * @create 2022/10/27 13:54
          */
         public class Test {
             public static void main(String[] args) {
                 int num = -2;
                 while (num < 0) {
                     num++;
                     System.out.println("num = " + num);
                 }
             }
         }
      • 输出

         num = -2
         num = -1
    • do while的使用

      • 模板

         do{
           语句;
         }while(循环条件);
      • 例如

         /**
          * @Author : zxl
          * @create 2022/10/27 13:54
          */
         public class Test {
             public static void main(String[] args) {
                 int num = -2;
         ​
                 do {
                     num ++;
                     System.out.println("num = " + num);
                 } while (num < 0);
             }
         }
      • 输出

         num = -1
         num = 0

  • for循环

    • for 循环语句是支持迭代的一种通用结构, 利用每次迭代之后更新的计数器或循环变量来控制迭代次数

    • 模板

       for(计数器初始化;循环条件;更新计数器)
       {
       语句块;
       }
    • 例如

       // 遍历输入0~9
       public class Test {
           public static void main(String[] args) {
               for (int i = 0; i < 10; i++) {
                   System.out.println("i = " + i);
               }
           }
       }
    • 解析

      • int i = 0; 表示计数器初始化

      • i < 10; 表示循环条件

      • i++; 表示更新了计数器

    • 在循环中,检测两个浮点数是否相等需要格外小心

      • 例如: for (double x = 0; x != 10; x += 0 . 1)

      • 可能永远不会结束。因为 0.1 无法精确地用二进制表示, 所以 x 将从 9.999 999 999 999 98 跳到

        10.099 999 999 999 9

    • for 循环语句只不过是 while 循环的一种简化

      • 例如

         public class Test {
             public static void main(String[] args) {
                 for (int i = 0; i < 10; i++) {
                     System.out.println("i = " + i);
                 }
             }
         }
      • 可以转换为

         public class Test {
             public static void main(String[] args) {
                 int i = 0;
                 while (i < 10) {
                     System.out.println("i = " + i);
                     i++;
                 }
             }
         }

  • 多重选择

    • 在处理多个选项时, 使用 if/else 结构显得有些笨拙。 Java 有一个与 C/C++ 完全一样的switch 语句

    • 语句结构

       int a;
       switch(a)
       {
           case 1 : ...break;
           case 2 : ...break;
           case 3: ...break;
           default: ... brak;
       }
       ​
      • switch、case、break、default都是关键词。switch作为一个开关,switch语句将从与表达式值相匹配的 case 标签处开始执行直到遇到 break 语句,或者执行到switch语句的结束处为止。如果没有相匹配的 case 标签, 而有 default 子句, 就执行这个子句。

    • 标签限制

      • 类型为 char、byte、 short 或 int 的常量表达式

      • 枚举常量

      • 从 Java SE 7开始, case 标签还可以是字符串字面量

  • 中断

    • 前言:无限制地使用 goto 语句确实是导致错误的根源,但在有些情况下,偶尔使用 goto 跳出循环还是有益处的,Java增加了一条带标签的 break来支持这种程序设计风格

    • 不带标签的 break 语句

       public class Test {
           public static void main(String[] args) {
               label:
               {
                   System.out.println("进入标签");
                   for (int i = 0; i < 10; i++) {
                       if (i == 5) {
                           System.out.println("触发跳出标签");
                           System.out.println("i = " + i);
                           break label;
                       }
       ​
                   }
               }
       ​
               System.out.println("结束");
       ​
           }
       }
    • 最后,还有一个 continue 语句。与 break 语句一样, 它将中断正常的控制流程

       public class Test {
           public static void main(String[] args) {
               for (int i = 0; i < 10; i++) {
                   if (i != 9) {
                       continue;
                   }
                   System.out.println("i = " + i);// i = 9
               }
           }
       }


大数值

  • java.math 包中的两个很有用的类:Biglnteger 和 BigDecimaL 这两个类可以处理包含任意长度数字序列的数值。Biglnteger 类实现了任意精度的整数运算, BigDecimal 实现了任意精度的浮点数运算

  • 示例如下:

     /**
      * @Author zxl
      * @Created 2022/10/28 11:42 
      */
     public class Test {
         public static void main(String[] args) {
             // 使用静态方法valueOf可以将普通数值转为大数值
             BigInteger a = BigInteger.valueOf(100);
             BigInteger b = BigInteger.valueOf(50);
     ​
             // 不过平时的+-*/ 不能使用了, 需要使用对应的实例方法
             // 加法 add
             BigInteger c = a.add(b);// 100 + 50
             System.out.println("c = " + c);// c = 150
     ​
             // 减法 subtract
             BigInteger d = a.subtract(b);// 100 - 50
             System.out.println("d = " + d);// d = 50
     ​
             // 乘法 multiply
             BigInteger e = a.multiply(b);// 100 * 50
             System.out.println("e = " + e);// e = 5000
     ​
             // 除法 divide
             BigInteger f = a.divide(b);// 100 / 50
             System.out.println("f = " + f);// f = 2
     ​
             // 如果两个大整数相等返回 0; 如果前者大于后者返回整数,否则返回负数
             int compare = a.compareTo(b); // a:100 比 b:50 大
             System.out.println("compare = " + compare);// compare = 1
     ​
             // 返回 2/(10的2次幂) 作为大实数
             BigDecimal d1 = BigDecimal.valueOf(2, 2);
             System.out.println("d1 = " + d1);// d1 = 0.02
         }
     }
  • 运算符重载问题

    • 与 C++ 不同, Java 没有提供运算符重载功能。 程序员无法重定义这些算数运算符,不过Java确实为字符串的连接重载了 + 运算符,但没有重载其他的运算符,也没有给 Java 程序员在自己的类中重载运算符的机会


数组

  • 前言

    • 数组是一种数据结构, 用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值, 例如

       // 声明
       int[] a;
       // 初始化  创建了一个可以存储 10 个整数的数组。数组长度不要求是常量
       a = new int[10];
       ​
       // 另一种声明方式  建议使用第一种声明,把int[] 类型 和 变量名a区分开来更易读
       int b [];
    • 数组的下标从 0 ~ 9 (不是 1 ~ 100)。一旦创建了数组,就可以给数组元素赋值。例如,使用一个循环

       int[] a = new int[10];
       for (int i = 0; i < a.length; i++) {
               a[i] = i;
       }
       ​
       // 遍历输出
        for (int i = 0; i < a.length; i++) {
            System.out.println("a[i] = " + a[i]);
       }
       ​
       //输出结果
       a[i] = 0
       a[i] = 1
       a[i] = 2
       a[i] = 3
       a[i] = 4
       a[i] = 5
       a[i] = 6
       a[i] = 7
       a[i] = 8
       a[i] = 9

    • 创建一个数字数组时,所有元素都初始化为 0。boolean 数组的元素会初始化为 false, 对象数组的元素则初始化为一个特殊值 null, null表示这些元素(还)未存放任何对象。

  • for each

    • Java增强版for循环,无指定下标

    • 语法如下:

       for(ElementsType element:Array){
          //operation
       }
    • 例如:

       public class Test {
           public static void main(String[] args) {
               int[] ints = {1, 2, 3, 4, 5};
               for (int i : ints) {
                   System.out.println("i = " + i);
               }
           }
       }
    • 还可以用更加简单的方式打印数组中的所有值, 即利用 Arrays 类的 toString 方法。例如:

       public class Test {
           public static void main(String[] args) {
               int[] ints = {1, 2, 3, 4, 5};
               // Arrays.toString(ints) = [1, 2, 3, 4, 5]
               System.out.println("Arrays.toString(ints) = " + Arrays.toString(ints));
           }
       }

  • 数组拷贝

    • 允许将一个数组变量拷贝给另一个数组变量。这时两个变量将引用同一个数组

       public class Test {
           public static void main(String[] args) {
               int[] a = {1, 2, 3, 4};
               int[] b = {1, 2, 3, 4};
               System.out.println("Arrays.toString(a) = " + Arrays.toString(a));
               System.out.println("Arrays.toString(b) = " + Arrays.toString(b));
       ​
               // a,b指向同一个地址
               System.out.println("a = " + a);//a = [I@4dcbad 匿名数组的地址值, 已经存放在a变量中
               System.out.println("b = " + b);//b = [I@4dcbad  匿名数组的地址值, 已经存放在b变量中
               System.out.println(a == b);// true
               // 与==等价,因为int并没有重写equals方法
               System.out.println(a.equals(b));// true
           }
       }

    • 如果将一个数组的所有值拷贝到一个新的数组中去,就要使用 Arrays 类的 copyOf方法

       public class Test {
           public static void main(String[] args) {
               int[] a = {1, 2, 3, 4};
               int[] b;
               // 传入被拷贝数组和长度
               b = Arrays.copyOf(a, a.length + 1);
       ​
               // b的数组长度为 5, 但是只有前四个被赋值了,由于b是数值数值,第五个默认为0
               System.out.println("Arrays.toString(b) = " + Arrays.toString(b));//Arrays.toString(b) = [1, 2, 3, 4, 0]
       ​
               // 如果长度小于原始数组的长度,则只拷贝最前面的数据元素
               int[] c;
               c = Arrays.copyOf(a, a.length - 2);
               //Arrays.toString(c ) = [1, 2]
               System.out.println("Arrays.toString(c ) = " + Arrays.toString(c));
       ​
           }

    • 注意: Java 中的 [ ] 运算符被预定义为检查数组边界,而且没有指针运算, 即不能通过 a 加

      1 得到数组的下一个元素

  • 命令行参数

    • 前言:每一个 Java 应用程序都有一个带 String arg[]参数的 main 方法。这个参数表明 main 方法将接收一个字符串数组, 也就是命令行参数

    • 可以选择在idea中, 右键选择Edit Configurations, 如图所示

       public class Test {
           public static void main(String... args) {
               for (String arg : args) {
                   System.out.println("arg = " + arg);
               }
           }
       }
    • 输出结果

       arg = hello
       arg = world

  • 数组排序

    • 要想对数值型数组进行排序, 可以使用 Arrays 类中的 sort 方法

       public class Test {
           public static void main(String... args) {
               int[] a = {1, 45, 23, 79, 36, 97, 5, 9};
               // 这个方法使用了优化的快速排序算法
               Arrays.sort(a);
               // Arrays.toString(a) = [1, 5, 9, 23, 36, 45, 79, 97]
               System.out.println("Arrays.toString(a) = " + Arrays.toString(a));
           }
       }
    • 顺便介绍下Arrays工具类的常用方法

       public class Test {
           public static void main(String... args) {
               int[] a = {1, 45, 23, 79, 36, 97, 5, 9};
       ​
               // toString()返回包含对应数据类型的字符串,元素用逗号分隔。
               //a = [1, 45, 23, 79, 36, 97, 5, 9]
               System.out.println("a" + Arrays.toString(a));
       ​
               // 返回与a类型相同的一个数组,并指定拷贝的起始下标和终止下标
               int[] b = Arrays.copyOfRange(a, 1, 4);
               System.out.println("b = " + Arrays.toString(b));// b = [45, 23, 79]
       ​
               //采用二分搜索算法查找指定元素值。如果查找成功返回相应的下标值; 否则返回一个负数值
               int index = Arrays.binarySearch(a, 45);
               System.out.println("index = " + index);//index = 1
       ​
               //将a数组所有元素都填充为9
               Arrays.fill(a, 9);
               System.out.println("a = " + Arrays.toString(a));//a = [9, 9, 9, 9, 9, 9, 9, 9]
               
           }
       }

  • 多维数组

    • 前言:Java 实际上没有多维数组,只有一维数组。多维数组只是被解释为“ 数组的数组“

    • 二维数组的声明初始化以及输出打印

       package com.legendshop.offical.api.service;
       ​
       import java.util.Arrays;
       ​
       /**
        * @Author zxl
        * @Created 2022/10/28 15:37 
        */
       public class Test {
           public static void main(String... args) {
               // 第一个[]必须有值, 第二个允许不填
               int[][] ints = new int[5][];
       ​
               // 如果知道数组元素,就可以不调用new,以简化形式对多维数组进行初始化
               // 这里其实可以说是不规则数组了
               int[][] a = {
                       {1, 2, 3},
                       {2, 4, 5},
                       {3, 4, 5, 6}
               };
       ​
               //第一种打印方式
               for (int i = 0; i < a.length; i++) {
                   for (int j = 0; j < a[i].length; j++) {
                       System.out.print(a[i][j]);
                   }
                   System.out.println();
               }
               //输出结果
               /*123
                 245
                 3456*/
       ​
       ​
               // for each打印
               for (int i = 0; i < a.length; i++) {
                   System.out.println(i + "-----" + Arrays.toString(a[i]));
               }
               //输出结果
       /*      0-----[1, 2, 3]
               1-----[2, 4, 5]
               2-----[3, 4, 5, 6]*/
       ​
               // 更简便的方式,deepToString方法直接打印二维数组
               String deepToString = Arrays.deepToString(a);
               // deepToString = [[1, 2, 3], [2, 4, 5], [3, 4, 5, 6]]
               System.out.println("deepToString = " + deepToString);
           }
       }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值