Java编程思想之字符串

1 不可变String

String 对象是不可变的。String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。

public class Immutable{
    public static String upcase(String s){
        return s.toUpperCase();
    }
    public static void main(String[] args){
        String q = "hello";
        System.out.println(q);
        String qq = upcase(q);
        System.out.println(qq);
        System.out.println(q);
    }
}
/*
运行结果:
hello
HELLO
hello
*/

当把q传给upcase()方法时,实际传递的是引用的一个拷贝。其实,每当把String对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。
upcase()返回的引用已经指向了一个新的对象,而原本的q则还在原地。

2 重载“+”与StringBuilder

* String对象具有只读特性,所以指向它的任何引用都不可能改变它的值。*
不可变性会带来一定的效率问题。为String对象重载的“+”操作符就是一个例子。重载的意思是,一个操作符在应用于特定的类时,被赋予了特殊的意义(用于String的“+”与“+=”是Java中仅有的两个重载过的操作符)。

public class Concatenation{
    public static void main(String[] args){
        String mm = "mm";
        String s = "1haha"+mm+"2ss"+55;
        System.out.println(s);
    }
}
/*
*执行javap -c Concatenation 反编译以上代码 生成JVM字节码
*/
/*
public class Concatenation {
  public Concatenation();
    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 mm
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: ldc           #5                  // String 1haha
      12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)L
      15: aload_1
      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)L
      19: ldc           #7                  // String 2ss
      21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)L
      24: bipush        55
      26: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringB
      29: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String
      32: astore_2
      33: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
      36: aload_2
      37: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      40: return
}
*/

编译器自动引入了StringBuilder类。因为它更高效。显示地创建StringBuilder还允许你预先为其指定大小,可以避免多次重新分配缓冲。StringBuilder re = new StringBuilder()
当你为一个类编写toString()方法时,如果字符串操作比较简单,那就可以信赖编译器,它为你合理地构造最终的字符串结果(使用“+”)。但是,如果你要在toString() 方法中使用循环,那么最好自己创建一个StringBuilder对象,用它来构造最终的结果。

3 无意识的递归

public class InfiniteRecursion{
    public String toString(){
        return "address:"+this+"\n";  //打印出对象的内存地址 出现异常
        // return super.toString(); 应该使用Object.toString()方法打印对象的地址。
    }
}

这里发生了自动类型转换,由InfiniteRecursion类型转换成String类型。因为编译器看到一个String对象后面跟着一个”+“,而再后面的对象不是String,于是编译器试着将this转换成一个String——通过调用this上的toString()方法。于是就发生了递归调用(无法正常结束)。

4 String上的操作

//部分方法尝试,其余的大多数方法都能够通过名字判断其用意
public class StringMethodTest{
    public static void main(String[] args){

        String s = "  hc hc  ";
        char[] aChars = new char[10];
        print("展示各类方法结果:");
        print("trim: "+s.trim());
        print("getChars:");
        s.getChars(1,5,aChars,2);
        for(char aa : aChars){
            System.out.print(aa);
        }
        System.out.println();
        print("regionMatches: "+s.regionMatches(1," ha",0,2)); //部分匹配 该String的索引偏移量,另一个String及其索引偏移量,要比较的长度
        print("replace: " +s.replace('c','h'));
        print("valueOf: "+s.valueOf(3));
        print("intern: "+s.intern());
    }

    public static void print(String s){
        System.out.println(s);
    }
}
/*展示各类方法结果:
trim: hc hc //只能删除两端的空白字符
getChars:
aa hc aaaa  //自动填充a
regionMatches: true  //部分匹配(应注意空白字符不能被忽略)
replace:   hh hh
valueOf: 3  //返回一个表示参数内容的String
intern:   hc hc
*/

当需要改变字符串的内容时,String类的方法都会返回一个新的String对象。同时,如果内容没有发生改变,String的方法只是返回指向原对象的引用而已。这可以节约存储空间以及避免额外的开销。

5 格式化输出

5.1 printf()

printf("Row 1: [%d %f]\n",x,y); //使用特殊的占位符(称为格式修饰符)来表示数据将来的位置,还可插入格式化字符串的参数。
在插入数据时,如果想要控制空格与对齐,需要更精细的格式修饰符 ,以下是其抽象语法:

  %[argument_index$][flags][width][.precision]conversion
  width 域的最小尺寸 默认数据是右对齐,可以是用"-"来改变对齐方向。
  precision 最大尺寸 只能用于String和浮点数(默认是六位小数)。
  举例:"%-10.5s" 占10个空格的位置,左对齐,只保留5个字符 String 
  String.format("%2$d + %5d",1,2);  返回的是2+    1。

5.2 System.out.format()和String.format()

System.out.format()与printf()是等价的。String.format()返回一个String对象,内部也是创建了一个Formatter对象。

5.3 Formatter类

在Java中,所有新的格式化功能都由java.util.Formatter类处理。可以将Formatter看作是一个翻译器,它将你的格式化字符串与数据翻译成需要的结果。当你创建一个Formatter对象的时候,需要向构造器传递一些信息,告诉它最终的结果将向哪里输出:

import java.util.*;
//ellipsis
    Formatter f = new Formatter(System.out);
    f.format()  //用法同上
类型转换字符:
d 整数型(十进制)e 浮点数(科学计数)
c Unicode字符x 整数(十六进制)
b Boolean值h 散列码(十六进制)
s String% 字符”%”
f 浮点数(十进制)
技术选型 【后端】:Java 【框架】:springboot 【前端】:vue 【JDK版本】:JDK1.8 【服务器】:tomcat7+ 【数据库】:mysql 5.7+ 项目包含前后台完整源码。 项目都经过严格调试,确保可以运行! 具体项目介绍可查看博主文章或私聊获取 助力学习实践,提升编程技能,快来获取这份宝贵的资源吧! 在当今快速发展的信息技术领域,技术选型是决定一个项目成功与否的重要因素之一。基于以下的技术栈,我们为您带来了一份完善且经过实践验证的项目资源,让您在学习和提升编程技能的道路上事半功倍。以下是该项目的技术选型和其组件的详细介绍。 在后端技术方面,我们选择了Java作为编程语言。Java以其稳健性、跨平台性和丰富的库支持,在企业级应用中处于领导地位。项目采用了流行的Spring Boot框架,这个框架以简化Java企业级开发而闻名。Spring Boot提供了简洁的配置方式、内置的嵌入式服务器支持以及强大的生态系统,使开发者能够更高效地构建和部署应用。 前端技术方面,我们使用了Vue.js,这是一个用于构建用户界面的渐进式JavaScript框架。Vue以其易上手、灵活和性能出色而受到开发者的青睐,它的组件化开发思想也有助于提高代码的复用性和可维护性。 项目的编译和运行环境选择了JDK 1.8。尽管Java已经推出了更新的版本,但JDK 1.8依旧是一种成熟且稳定的选择,广泛应用于各类项目中,确保了兼容性和稳定性。 在服务器方面,本项目部署在Tomcat 7+之上。Tomcat是Apache软件基金会下的一个开源Servlet容器,也是应用最为广泛的Java Web服务器之一。其稳定性和可靠的性能表现为Java Web应用提供了坚实的支持。 数据库方面,我们采用了MySQL 5.7+。MySQL是一种高效、可靠且使用广泛的关系型数据库管理系统,5.7版本在性能和功能上都有显著的提升。 值得一提的是,该项目包含了前后台的完整源码,并经过严格调试,确保可以顺利运行。通过项目的学习和实践,您将能更好地掌握从后端到前端的完整开发流程,提升自己的编程技能。欢迎参考博主的详细文章或私信获取更多信息,利用这一宝贵资源来推进您的技术成长之路!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值