高端网站建设的市场分析活动策划费用明细
高端网站建设的市场分析,活动策划费用明细,广州建站网络推广公司,长链接生成短链接网址一前言
今天正常更新#xff0c;内容不难但是东西很多#xff0c;是关于字符串的#xff0c;有很多函数#xff0c;而且比较长#xff0c;不过也不用太担心#xff0c;大部分是英文直译#xff0c;所以做好笔记。
二主要内容
字符串和编码
String
在Java中#xf…一前言今天正常更新内容不难但是东西很多是关于字符串的有很多函数而且比较长不过也不用太担心大部分是英文直译所以做好笔记。二主要内容字符串和编码String在Java中String是一个引用类型它本身也是一个class。但是Java编译器对String有特殊处理即可以直接用...来表示一个字符串String s1 Hello!;实际上字符串在String内部是通过一个char[]数组表示的因此按下面的写法也是可以的String s2 new String(new char[] {H, e, l, l, o, !});因为String太常用了所以Java提供了...这种字符串字面量表示方法。Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的private final char[]字段以及没有任何修改char[]的方法实现的。我们来看一个例子// String public class Main { public static void main(String[] args) { String s Hello; System.out.println(s); s s.toUpperCase(); System.out.println(s); } }根据上面代码的输出试解释字符串内容是否改变。字符串比较当我们想要比较两个字符串是否相同时要特别注意我们实际上是想比较字符串的内容是否相同。必须使用equals()方法而不能用。我们看下面的例子// String public class Main { public static void main(String[] args) { String s1 hello; String s2 hello; System.out.println(s1 s2); System.out.println(s1.equals(s2)); } }从表面上看两个字符串用和equals()比较都为true但实际上那只是Java编译器在编译期会自动把所有相同的字符串当作一个对象放入常量池自然s1和s2的引用就是相同的。所以这种比较返回true纯属巧合。换一种写法比较就会失败// String public class Main { public static void main(String[] args) { String s1 hello; String s2 HELLO.toLowerCase(); System.out.println(s1 s2); System.out.println(s1.equals(s2)); } }结论两个字符串比较必须总是使用equals()方法。要忽略大小写比较使用equalsIgnoreCase()方法。String类还提供了多种方法来搜索子串、提取子串。常用的方法有// 是否包含子串: Hello.contains(ll); // true注意到contains()方法的参数是CharSequence而不是String因为CharSequence是String实现的一个接口。搜索子串的更多的例子Hello.indexOf(l); // 2 Hello.lastIndexOf(l); // 3 Hello.startsWith(He); // true Hello.endsWith(lo); // true提取子串的例子Hello.substring(2); // llo Hello.substring(2, 4); ll注意索引号是从0开始的。去除首尾空白字符使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格\t\r\n \tHello\r\n .trim(); // Hello注意trim()并没有改变字符串的内容而是返回了一个新字符串。另一个strip()方法也可以移除字符串首尾空白字符。它和trim()不同的是类似中文的空格字符\u3000也会被移除\u3000Hello\u3000.strip(); // Hello Hello .stripLeading(); // Hello Hello .stripTrailing(); // HelloString还提供了isEmpty()和isBlank()来判断字符串是否为空和空白字符串.isEmpty(); // true因为字符串长度为0 .isEmpty(); // false因为字符串长度不为0 \n.isBlank(); // true因为只包含空白字符 Hello .isBlank(); // false因为包含非空白字符替换子串要在字符串中替换子串有两种方法。一种是根据字符或字符串替换String s hello; s.replace(l, w); // hewwo所有字符l被替换为w s.replace(ll, ~~); // he~~o所有子串ll被替换为~~另一种是通过正则表达式替换String s A,,B;C ,D; s.replaceAll([\\,\\;\\s], ,); // A,B,C,D上面的代码通过正则表达式把匹配的子串统一替换为,。关于正则表达式的用法我们会在后面详细讲解。分割字符串要分割字符串使用split()方法并且传入的也是正则表达式String s A,B,C,D; String[] ss s.split(\\,); // {A, B, C, D}拼接字符串拼接字符串使用静态方法join()它用指定的字符串连接字符串数组String[] arr {A, B, C}; String s String.join(***, arr); // A***B***C格式化字符串字符串提供了formatted()方法和format()静态方法可以传入其他参数替换占位符然后生成新的字符串// String public class Main { public static void main(String[] args) { String s Hi %s, your score is %d!; System.out.println(s.formatted(Alice, 80)); System.out.println(String.format(Hi %s, your score is %.2f!, Bob, 59.5)); } }有几个占位符后面就传入几个参数。参数类型要和占位符一致。我们经常用这个方法来格式化信息。常用的占位符有%s显示字符串%d显示整数%x显示十六进制整数%f显示浮点数。占位符还可以带格式例如%.2f表示显示两位小数。如果你不确定用啥占位符那就始终用%s因为%s可以显示任何数据类型。要查看完整的格式化语法请参考JDK文档。类型转换要把任意基本类型或引用类型转换为字符串可以使用静态方法valueOf()。这是一个重载方法编译器会根据参数自动选择合适的方法String.valueOf(123); // 123 String.valueOf(45.67); // 45.67 String.valueOf(true); // true String.valueOf(new Object()); // 类似java.lang.Object636be97c要把字符串转换为其他类型就需要根据情况。例如把字符串转换为int类型int n1 Integer.parseInt(123); // 123 int n2 Integer.parseInt(ff, 16); // 按十六进制转换255把字符串转换为boolean类型boolean b1 Boolean.parseBoolean(true); // true boolean b2 Boolean.parseBoolean(FALSE); // false要特别注意Integer有个getInteger(String)方法它不是将字符串转换为int而是把该字符串对应的系统变量转换为IntegerInteger.getInteger(java.version); // 版本号11转换为char[]String和char[]类型可以互相转换方法是char[] cs Hello.toCharArray(); // String - char[] String s new String(cs); // char[] - String如果修改了char[]数组String并不会改变// String - char[] public class Main { public static void main(String[] args) { char[] cs Hello.toCharArray(); String s new String(cs); System.out.println(s); cs[0] X; System.out.println(s); } }这是因为通过new String(char[])创建新的String实例时它并不会直接引用传入的char[]数组而是会复制一份所以修改外部的char[]数组不会影响String实例内部的char[]数组因为这是两个不同的数组。从String的不变性设计可以看出如果传入的对象有可能改变我们需要复制而不是直接引用。例如下面的代码设计了一个Score类保存一组学生的成绩// int[] import java.util.Arrays; public class Main { public static void main(String[] args) { int[] scores new int[] { 88, 77, 51, 66 }; Score s new Score(scores); s.printScores(); scores[2] 99; s.printScores(); } } class Score { private int[] scores; public Score(int[] scores) { this.scores scores; } public void printScores() { System.out.println(Arrays.toString(scores)); } }观察两次输出由于Score内部直接引用了外部传入的int[]数组这会造成外部代码对int[]数组的修改影响到Score类的字段。如果外部代码不可信这就会造成安全隐患。请修复Score的构造方法使得外部代码对数组的修改不影响Score实例的int[]字段。字符编码在早期的计算机系统中为了给字符编码美国国家标准学会American National Standard InstituteANSI制定了一套英文字母、数字和常用符号的编码它占用一个字节编码范围从0到127最高位始终为0称为ASCII编码。例如字符A的编码是0x41字符1的编码是0x31。如果要把汉字也纳入计算机编码很显然一个字节是不够的。GB2312标准使用两个字节表示一个汉字其中第一个字节的最高位始终为1以便和ASCII编码区分开。例如汉字中的GB2312编码是0xd6d0。类似的日文有Shift_JIS编码韩文有EUC-KR编码这些编码因为标准不统一同时使用就会产生冲突。为了统一全球所有语言的编码全球统一码联盟发布了Unicode编码它把世界上主要语言都纳入同一个编码这样中文、日文、韩文和其他语言就不会冲突。Unicode编码需要两个或者更多字节表示我们可以比较中英文字符在ASCII、GB2312和Unicode的编码英文字符A的ASCII编码和Unicode编码┌────┐ ASCII: │ 41 │ └────┘ ┌────┬────┐ Unicode: │ 00 │ 41 │ └────┴────┘英文字符的Unicode编码就是简单地在前面添加一个00字节。中文字符中的GB2312编码和Unicode编码┌────┬────┐ GB2312: │ d6 │ d0 │ └────┴────┘ ┌────┬────┐ Unicode: │ 4e │ 2d │ └────┴────┘那我们经常使用的UTF-8又是什么编码呢因为英文字符的Unicode编码高字节总是00包含大量英文的文本会浪费空间所以出现了UTF-8编码它是一种变长编码用来把固定长度的Unicode编码变成14字节的变长编码。通过UTF-8编码英文字符A的UTF-8编码变为0x41正好和ASCII码一致而中文中的UTF-8编码为3字节0xe4b8ad。UTF-8编码的另一个好处是容错能力强。如果传输过程中某些字符出错不会影响后续字符因为UTF-8编码依靠高字节位来确定一个字符究竟是几个字节它经常用来作为传输编码。在Java中char类型实际上就是两个字节的Unicode编码。如果我们要手动把字符串转换成其他编码可以这样做byte[] b1 Hello.getBytes(); // 按系统默认编码转换不推荐 byte[] b2 Hello.getBytes(UTF-8); // 按UTF-8编码转换 byte[] b2 Hello.getBytes(GBK); // 按GBK编码转换 byte[] b3 Hello.getBytes(StandardCharsets.UTF_8); // 按UTF-8编码转换注意转换编码后就不再是char类型而是byte类型表示的数组。如果要把已知编码的byte[]转换为String可以这样做byte[] b ... String s1 new String(b, GBK); // 按GBK转换 String s2 new String(b, StandardCharsets.UTF_8); // 按UTF-8转换始终牢记Java的String和char在内存中总是以Unicode编码表示。延伸阅读对于不同版本的JDKString类在内存中有不同的优化方式。具体来说早期JDK版本的String总是以char[]存储它的定义如下public final class String { private final char[] value; private final int offset; private final int count; }而较新的JDK版本的String则以byte[]存储如果String仅包含ASCII字符则每个byte存储一个字符否则每两个byte存储一个字符这样做的目的是为了节省内存因为大量的长度较短的String通常仅包含ASCII字符public final class String { private final byte[] value; private final byte coder; // 0 LATIN1, 1 UTF16对于使用者来说String内部的优化不影响任何已有代码因为它的public方法签名是不变的。小结Java字符串String是不可变对象字符串操作不改变原字符串内容而是返回新字符串常用的字符串操作提取子串、查找、替换、大小写转换等Java使用Unicode编码表示String和char转换编码就是将String和byte[]转换需要指定编码转换为byte[]时始终优先考虑UTF-8编码。StringBuilder 用处不多Java编译器对String做了特殊处理使得我们可以直接用拼接字符串。考察下面的循环代码String s ; for (int i 0; i 1000; i) { s s , i; }虽然可以直接拼接字符串但是在循环中每次循环都会创建新的字符串对象然后扔掉旧的字符串。这样绝大部分字符串都是临时对象不但浪费内存还会影响GC效率。为了能高效拼接字符串Java标准库提供了StringBuilder它是一个可变对象可以预分配缓冲区这样往StringBuilder中新增字符时不会创建新的临时对象StringBuilder sb new StringBuilder(1024); for (int i 0; i 1000; i) { sb.append(,); sb.append(i); } String s sb.toString();StringBuilder还可以进行链式操作// 链式操作 public class Main { public static void main(String[] args) { var sb new StringBuilder(1024); sb.append(Mr ) .append(Bob) .append(!) .insert(0, Hello, ); System.out.println(sb.toString()); } }如果我们查看StringBuilder的源码可以发现进行链式操作的关键是定义的append()方法会返回this这样就可以不断调用自身的其他方法。仿照StringBuilder我们也可以设计支持链式操作的类。例如一个可以不断增加的计数器// 链式操作 public class Main { public static void main(String[] args) { Adder adder new Adder(); adder.add(3) .add(5) .inc() .add(10); System.out.println(adder.value()); } } class Adder { private int sum 0; public Adder add(int n) { sum n; return this; } public Adder inc() { sum ; return this; } public int value() { return sum; } }注意对于普通的字符串操作并不需要我们将其改写为StringBuilder因为Java编译器在编译时就自动把多个连续的操作编码为StringConcatFactory的操作。在运行期StringConcatFactory会自动把字符串连接操作优化为数组复制或者StringBuilder操作。你可能还听说过StringBuffer这是Java早期的一个StringBuilder的线程安全版本它通过同步来保证多个线程操作StringBuffer也是安全的但是同步会带来执行速度的下降。StringBuilder和StringBuffer接口完全相同现在完全没有必要使用StringBuffer。StringJoiner要高效拼接字符串应该使用StringBuilder。很多时候我们拼接的字符串像这样// 输出: Hello Bob, Alice, Grace! public class Main { public static void main(String[] args) { String[] names {Bob, Alice, Grace}; var sb new StringBuilder(); sb.append(Hello ); for (String name : names) { sb.append(name).append(, ); } // 注意去掉最后的, : sb.delete(sb.length() - 2, sb.length()); sb.append(!); System.out.println(sb.toString()); } }类似用分隔符拼接数组的需求很常见所以Java标准库还提供了一个StringJoiner来干这个事import java.util.StringJoiner; public class Main { public static void main(String[] args) { String[] names {Bob, Alice, Grace}; var sj new StringJoiner(, ); for (String name : names) { sj.add(name); } System.out.println(sj.toString()); } }慢着用StringJoiner的结果少了前面的Hello 和结尾的!遇到这种情况需要给StringJoiner指定“开头”和“结尾”import java.util.StringJoiner; public class Main { public static void main(String[] args) { String[] names {Bob, Alice, Grace}; var sj new StringJoiner(, , Hello , !); for (String name : names) { sj.add(name); } System.out.println(sj.toString()); } }String.join()String还提供了一个静态方法join()这个方法在内部使用了StringJoiner来拼接字符串在不需要指定“开头”和“结尾”的时候用String.join()更方便String[] names {Bob, Alice, Grace}; var s String.join(, , names);三最后一语今天的内容偏向定义所以看起来较多需要每一个都自己试一试加深印象等待是植根于孤独之中的植物吧孤独越强大等待越茂盛。——李娟《遥远的向日葵地》感谢观看共勉