跳至主要內容

Java - 数组 | 字符串 | 正则表达式

codejava约 2929 字大约 10 分钟

面向对象高级篇 2

数组

数组是相同类型数据的有序集合,数组可以代表任何相同类型的一组内容(包括引用类型和基本类型)其中存放的每一个数据称为数组的一个元素。

定义

数组类型比较特殊,它本身也是类,但是编程不可见(底层C++写的,在运行时动态创建)
即使是基本类型的数组,也是以对象的形式存在的,并不是基本数据类型。所以,我们要创建一个数组,同样需要使用 new 关键字

public static void main(String[] args) {
    int[] array = new int[10];   //类型[]就表示这个是一个数组类型
    Object obj = array;   //因为同样是类,肯定是继承自Object的,所以说可以直接向上转型
}

创建出来的数组每个位置上都有默认值,如果是引用类型,就是null,如果是基本数据类型,就是0,或者是false,跟对象成员变量的默认值是一样的
其他定义方法:

类型[] 变量名称 = new 类型[数组大小];
类型 变量名称[] = new 类型[数组大小];  //支持C语言样式,但不推荐!

类型[] 变量名称 = new 类型[]{...};  //静态初始化(直接指定值和大小)
类型[] 变量名称 = {...};   //同上,但是只能在定义时赋值

方法

数组的 length 是在一开始就确定的,而且是 final类型 的,不允许进行修改,也就是说数组的长度一旦确定,不能随便进行修改,如果需要使用更大的数组,只能重新创建。

  public static void main(String[] args) {
      int[] array = new int[10];
      System.out.println("当前数组长度为:"+array.length);   //length属性是int类型的值,表示当前数组长度,长度是在一开始创建数组的时候就确定好的
  }

array 虽然是继承于 Object,但是,很遗憾,除了clone()之外,这些方法并没有被重写,也就是说依然是采用的Object中的默认实现,所以可能不满足真实需求。

访问元素

for 或者 foreach

  public static void main(String[] args) {
      int[] array = new int[10];
      for (int i = 0; i < array.length; i++) {
          System.out.print(array[i] + " ");
      }
      for (int i : array) {    //int i就是每一个数组中的元素,array就是我们要遍历的数组
        System.out.print(i+" ");   //每一轮循环,i都会更新成数组中下一个元素
    }
  }

特性

这里需要特别说一下,对于基本类型的数组来说,是不支持自动装箱和拆箱的:

  public static void main(String[] args) {
      int[] arr = new int[10];
      Integer[] test = arr;  // 这样是不能赋值的 会报错
  }

由于基本数据类型和引用类型不同,所以说int类型的数组时不能被Object类型的数组变量接收的, 即 int[] arr = new int[10] 是不能 Object[] arrav = arr 这样的

但是如果是引用类型的话,是可以的,因为父类都是 Object

  public static void main(String[] args) {
      String[] arr = new String[10];
      Object[] array = arr;    //数组同样支持向上转型

      Object[] arr = new Object[10];
      String[] array = (String[]) arr;   //也支持向下转型
  }

final性质

  public static void main(String[] args) {
      final int[] a = {1,2,3,4};
      // 值还是可以修改,只是数组的地址不准修改
      a[0] = 4;  // 允许
      a = {2,4,8,7};  //不允许
  }

多维数组

既然数组可以是任何类型的,那么我们能否创建数组类型的数组呢?答案是可以的

public static void main(String[] args) {
    int[][] arr = { {1, 2},
                    {3, 4},
                    {5, 6}};   //一个三行两列的数组
    System.out.println(arr[2][1]);   //访问第三行第二列的元素
}

可变长参数

public void function(参数类型...参数名称)
这样参数名称所表示的就是一个数组

public class Person {
    String name;
    int age;
    String sex;

    // 可以传入 0 - N 个类型的实参
    public void test(String... strings){
      //strings这个变量就是一个String[]类型的
      for (String string : strings) {
          System.out.println(string);   //遍历打印数组中每一个元素
      } 
    }
}

public static void main(String[] args) {
    Person person = new Person();
    person.test("1!", "5!", "哥们在这跟你说唱"); //这里我们可以自由传入任意数量的字符串
}

注意,如果同时存在其他参数,那么可变长参数只能放在最后:

public void test(int a, int b, String... strings){
    
}

main函数的 String[] args

public static void main(String[] args) {
    for (String arg : args) {
        System.out.println(arg);
    }
}

可以看到,默认情况下直接运行什么都没有,但是如果我们在运行时,添加点内容的话:
java com/test/Main lbwnb aaaa xxxxx
因此会读取命令行中的指令参数进行存储到 args 中。

字符串

字符串类是一个比较特殊的类,它用于保存字符串。 我们知道,基本类型 char 可以保存一个2字节的Unicode字符,而字符串则是一系列字符的序列(在C中就是一个字符数组)
Java中没有字符串这种基本类型,因此只能使用类来进行定义。 注意,字符串中的字符一旦确定,无法进行修改,只能重新创建。

String 类

String本身也是一个类,只不过它比较特殊,每个用双引号括起来的字符串,都是String类型的一个实例对象, 也可以象征性 new 不过没必要
如果是直接使用双引号创建的字符串,如果内容相同,为了优化效率,那么始终都是同一个对象
但是如果我们使用构造方法主动创建两个新的对象,那么就是不同的对象了

public static void main(String[] args) {
    String str1 = "Hello World";
    String str2 = "Hello World";
    System.out.println(str1 == str2);

    // 这样就不同
    String str3 = new String("Hello World");
    String str4 = new String("Hello World");
    System.out.println(str3 == str4);

    System.out.println(str1.equals(str2));   //字符串的内容比较,一定要用equals
}

因此,如果我们仅仅是想要判断两个字符串的内容是否相同,不要使用 ==
String类重载了equals方法用于判断和比较内容是否相同

获取长度 str.length()

public static void main(String[] args) {
    String str = "Hello World";
    System.out.println(str.length());   //length方法可以求字符串长度,这个长度是字符的数量
}

字符串类中提供了很多方便我们操作的方法, 比如字符串的裁剪 (substring)、分割操作 (split)

  public static void main(String[] args) {
      String str = "Hello World";
      String sub = str.substring(0, 3);   //分割字符串,并返回一个新的子串对象
      System.out.println(sub);

      String[] strings = str.split(" ");   //使用split方法进行字符串分割,比如这里就是通过空格分隔,得到一个字符串数组
      for (String string : strings) {
          System.out.println(string);
      }
  }

字符数组和字符串之间是可以快速进行相互转换的
字符串转字符数组: char[] chars = str.toCharArray()字符数组转字符串: String str = new String(chars)

StringBuilder 类

StringBuilder 就是专门用于构造字符串的,我们可以使用它来对字符串进行拼接、裁剪等操作,它就像一个字符串编辑器,弥补了字符串不能修改的不足

public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();   //一开始创建时,内部什么都没有
    builder.append("AAA");   //我们可以使用append方法来讲字符串拼接到后面
    builder.append("BBB");
    builder.delete(2, 4);   //删除2到4这个范围内的字符
    System.out.println(builder.toString());   //当我们字符串编辑完成之后,就可以使用toString转换为字符串了
}

字符串支持使用 ++= 进行拼接操作, 但是拼接字符串实际上底层需要进行很多操作,如果程序中大量进行字符串的拼接似乎不太好,编译器是很聪明的,String的拼接会在编译时进行各种优化:

对于变量来说

public static void main(String[] args) {
    String str1 = "你看";
    String str2 = "这";
    String str3 = "汉堡";
    String str4 = "做滴";
    String str5 = "行不行";
    String result = str1 + str2 + str3 + str4 + str5;   //5个变量连续加
    System.out.println(result);
}

如果直接使用加的话,每次运算都会生成一个新的对象,这里进行4次加法运算,那么中间就需要产生4个字符串对象出来,是不是有点太浪费了? 这种情况实际上会被优化为下面的写法:

public static void main(String[] args) {
    String str1 = "你看";
    String str2 = "这";
    String str3 = "汉堡";
    String str4 = "做滴";
    String str5 = "行不行";
    StringBuilder builder = new StringBuilder();
    builder.append(str1).append(str2).append(str3).append(str4).append(str5);
    System.out.println(builder.toString());
}

正则表达式

str.match(正则表达式)

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

public static void main(String[] args) {
    String str = "oooo";
    //matches方法用于对给定正则表达式进行匹配,匹配成功返回true,否则返回false
    System.out.println(str.matches("o+"));   //+表示对前面这个字符匹配一次或多次,这里字符串是oooo,正好可以匹配
}

用于规定给定组件必须要出现多少次才能满足匹配的,我们一般称为限定符,限定符表如下:

字符描述
*匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。***** 等价于 {0,}
+匹配前面的子表达式一次或多次。例如,zo+ 能匹配 "zo" 以及 "zoo",但不能匹配 "z"+ 等价于 {1,}
?匹配前面的子表达式零次或一次。例如,do(es)? 可以匹配 "do""does""doxy" 中的 "do"? 等价于 {0,1}
{n}n 是一个非负整数。匹配确定的 n 次。例如,o{2} 不能匹配 "Bob" 中的 o,但是能匹配 "food" 中的两个 o
{n,}n 是一个非负整数。至少匹配n 次。例如,o{2,} 不能匹配 "Bob" 中的 o,但能匹配 "foooood" 中的所有 oo{1,} 等价于 o+o{0,} 则等价于 o*
{n,m}m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。例如,o{1,3} 将匹配 "fooooood" 中的前三个 oo{0,1} 等价于 o?。请注意在逗号和两个数之间不能有空格。

如果我们想要表示一个范围内的字符,可以使用方括号:

public static void main(String[] args) {
    String str = "abcabccaa";
    System.out.println(str.matches("[abc]*"));   //表示abc这几个字符可以出现 0 - N 次
}

对于普通字符来说,我们可以下面的方式实现多种字符匹配:

字符描述
[ABC]匹配 [...] 中的所有字符,例如 [aeiou] 匹配字符串 "google runoob taobao" 中所有的 e o u a 字母。
[^ABC]匹配除了 [...] 中字符的所有字符,例如 [^aeiou] 匹配字符串 "google runoob taobao" 中除了 e o u a 字母的所有字母。
[A-Z][A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母。
.匹配除换行符(\n、\r)之外的任何单个字符,相等于 [^\n\r]
[\s\S]匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,不包括换行。
\w匹配字母、数字、下划线。等价于 [A-Za-z0-9_]

当然,这里仅仅是对正则表达式的简单使用,实际上正则表达式内容非常多,如果需要完整学习正则表达式,可以到:https://www.runoob.com/regexp/regexp-syntax.htmlopen in new window

正则表达式并不是只有Java才支持,其他很多语言比如JavaScript、Python等等都是支持正则表达式的。

上次编辑于: