1.不可变String
- 如果某个对象出现在字符串表达式中,其toString()方法会被自动调用。
- 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 = "howdy"; System.out.println(q); //将q改变 String qq = upcase(q); //输出qq,此时qq引用的是一个新的String对象,他使用了q所引用对象的内容 System.out.println(qq); //原来的对象还是原来q引用的对象并未改变 System.out.println(q); } } /** howdy HOWDY howdy */
- 参数为方法提供信息,而不是希望方法改变自己。
- 当调用String上的方法的改变String内容而返回新的String对象时,如果内容没有发生改变,String的方法只是返回指向原对象的引用而已。
2.重载 “+” 与StringBuilder
- 重载:一个操作符在应用于一个特殊的类时,被赋予了特殊的意义
用于String的“+”和“+=”是java中仅有的两个重载过的操作符,而java并不允许程序员重载任何操作符。 - String对象只具有只读特性,所有指向它的任何引用都不可能改变它的值,也不会对其他引用产生影响。
- 不可变性会带来一定的效率问题,比如为String重载的操作符“+”,如果不断的使用就会产生大量的中间String对象,如下:
如果按照原来的不可变性的话,每连接一个就生成一个新的String对象,就会产生很多的垃圾对象,但java已经对此进行了优化,如下。public class Concatenation { public static void main(String[] args) { String mango = "mango"; String s = "abc" + mango + "def" + 47; System.out.println(s); } }
通过反编译上述代码得到汇编语句,就可以得到String改变时生成新String的真源:
- 编译器会自动引入java.lang.StringBuilder类。虽然我们在源码中并没有使用StringBuilder类,但是编译器却自作主张的使用了它,因为它更高效。
- 这个例子中,编译器为每个“+”追加的字符串调用一次StringBuilder的append()方法,总共四次。最后调用toString()生成结果,并存为s.
- 这就是编译器的优化功能。
-
编译器的优化功能并不总是有效。
public class WhitherStringBuilder { public String implicit(String[] fields){ String result = ""; for(int i = 0; i < fields.length; i++) result += fields[i];//每进行一次“+”操作符运算,就会生成一个StringBuffer对象 return result; } public String explicit(String[] fields){ StringBuffer result = new StringBuffer(); for(int i = 0; i < fields.length; i++) result.append(fields[i]);//这样只会有一个StringBuffer对象 return result.toString(); } }
-
StringBuilder除了上面的作用,显式的创建StringBuilder允许预先为其指定大小,预先指定StringBuilder的大小可以避免多次重新分配缓存。
-
使用StringBuilder需要注意的地方:如果这样操作append(a+":"+c) ,编译器会掉入陷阱,从而为你另外创建一个StringBuilder对象处理括号内的字符串操作。
3.无意识递归toString()
public class InfiniteRecursion {
//toString()方法 ,将类转换成String
@Override
public String toString() {
/**
* 这儿会产生无意识的递归,因为java发现一个String后面放的不是一个String的话,
* java会调用这个对象的toString方法把这个对象转换成String类型,所以这里就会在调用这个toString()方法
* 产生了无意识的递归
*/
return "InfiniteRecursiong :" + this+"\n";
}
public static void main(String[] args) {
//直接打印 因为toStrig的无限递归,会抛出异常
System.out.println(new InfiniteRecursion());
}
}
4.打印对象的内存地址
- 如果你想打印对象的内存地址,应该调用Object.toString()方法。
5.格式化输出 System.out
public class SimpleFormat { public static void main(String[] args) { int x = 5; double y = 5.332542; //旧的方式 System.out.println("Row 1:[" + x + " " + y + "]"); //新的方式 第一个参数是打印格式 后面是参数列表 System.out.format("Row 1:[%d %f]\n", x,y); //或者 System.out.printf("Row 1:[%d %f]\n", x,y); } }
- 这种输入方式来自于C,使用特殊的占位符来表示数据将来的位置,而且还将插入格式化字符串的参数,以逗号隔开(如x,y),排成一行,(可变参数列表)
这行代码运行的时候,首先将x的值插入到d%的位置,然后将y的值插入f%的位置。这些占位符被称为格式修饰符,不但说明插入数据的位置,同时还说明了插入什么类型的变量,以及如何对其格式化。System.out.format("Row 1:[%d %f]\n", x,y);
%d是整数,%f是float或double.
6.Formatter类 字符串格式化输出
- 所有新的功能都由java.util.Formatter类处理。它将格式化字符串与数据翻译成需要的结果。
所有的tommy都将输出到System.out上,而terry都将输出到System.out的一个别名中。public class Turtle { private String name; private Formatter f; public Turtle(String name, Formatter f) { this.name = name; this.f = f; } public void move(int x, int y){ f.format("%s The Turtle is at (%d,%d)\n", name, x, y); } public static void main(String[] args) { PrintStream outAlias = System.out; Turtle tommy = new Turtle("Tommy", new Formatter(System.out)); Turtle terry = new Turtle("Treey", new Formatter(outAlias)); tommy.move(0, 0); terry.move(4, 8); tommy.move(3, 4); terry.move(2, 5); tommy.move(3, 3); terry.move(3, 3); } }
- Formatter的构造器经过重载可以接受多种输出目的地,不过最常用的还是PrintStream()、OutputStream和File
7.格式化说明符%[argument_index$][flags][width][.precision]conversion
- 格式化说明符:在插入数据时如果想控制空格和对齐,需要更精细复杂的格式修饰符。
:抽象语法:%[argument_index$][flags][width][.precision]conversion
- width:控制一个域的最小尺寸,可以通过width来实现。Formatter对象通过必要时添加空格,来确保一个域至少达到的某个长度
默认情况下数据是右对齐,可以使用“-”标志来改变对齐方向。width可以应用于各种类型的数据转换,并且其行为方式都是一样的。 - precision:与width相对,它用来指明最大尺寸,并且不是所有类型的数据都能使用precision,应用于不同类型的数据转换时,precision的意义也不一样
1:String时,他表示打印String输出字符的最大数量。
2:浮点数时:表示小数部分要显示出来的位数,如果位数过多则舍入,太少则在尾部补零。
3:整数时:会抛出异常,无法应用于整数。 -
字段
说明 argument_index 需要将参数列表中第几个参数进行格式化,用$进行间隔 flags
一些特殊的格式,比如‘-’代表向左对齐 width
输出的最小的宽度,适用于所有的参数类型 [.precision]
参数为String,则表示打印String输出字符的最大数量;参数为float,则表示小数点最大位数。不使用于int conversion
接受的参数类型,如s代表后面接String类型的参数;d代表接int型的参数 public class Receipt { private double total = 0; //格式化功能类 private Formatter f = new Formatter(System.out); public void printTitle(){ // :%[argument_index$][flags][width][.precision]conversion f.format("%-15s %5s %10s\n", "Item","Qty","Price"); f.format("%-15s %5s %10s\n", "----","---","-----"); } public void print(String name, int qty, double price){ f.format("%-15.15s %5d %10.1f\n", name, qty, price); total += price; } public void printTotal(){ f.format("%-15s %5s %10.2f\n", "Tax", "", total*0.06); f.format("%-15s %5s %10s\n", "Tax", "", "-----"); f.format("%-15s %5s %10.2f\n", "Total", "", total*0.06); } public static void main(String[] args) { Receipt receipt = new Receipt(); receipt.printTitle(); receipt.print("Jack's Magic Beans", 4, 4.25); receipt.print("Princess Peas", 3, 5.1); receipt.print("Three Bears Porridge", 1, 14.29); receipt.printTotal(); } } /** Item Qty Price ---- --- ----- Jack's Magic Be 4 4.3 Princess Peas 3 5.1 Three Bears Por 1 14.3 Tax 1.42 Tax ----- Total 1.42 */
- width:控制一个域的最小尺寸,可以通过width来实现。Formatter对象通过必要时添加空格,来确保一个域至少达到的某个长度
8.Formatter转换 异常补充
- java类型转换字符
public class Conversion { public static void main(String[] args) { Formatter f = new Formatter(System.out); char u = 'a'; System.out.println("u = 'a'"); /** * 举例如下 */ //转换成字符串 f.format("s %s\n", u); //转换布尔型 f.format("b: %b\n", u); //不常见的 转换成散列码 f.format("h: %h\n", u); //下面几种转换是无效的,会抛出异常 f.format("f: %f\n", u);//到这儿就会抛出异常下面的代码不会执行,<span style="color:#CC0000;">除非捕获异常把下面代码写在finally或catch里</span> f.format("e: %e\n", u); f.format("x: %x\n", u); } } /** u = 'a' s a b: true h: 61 f: Exception in thread "main" java.util.IllegalFormatConversionException: f != java.lang.Character at java.util.Formatter$FormatSpecifier.failConversion(Unknown Source) at java.util.Formatter$FormatSpecifier.printFloat(Unknown Source) at java.util.Formatter$FormatSpecifier.print(Unknown Source) at java.util.Formatter.format(Unknown Source) at java.util.Formatter.format(Unknown Source) at com.yue.string.Conversion.main(Conversion.java:21) */
- 注意:当其他类型转换为boolean时,只要该值不为空,就永远返回true。对于Boolean对象或boolean的值,其转换结果是true或false。
9.String.format
public class DatabaseException extends Exception{ public DatabaseException(int transactionID, int queryID, String message) { //一个String.format super(String.format("(t%d, q%d) %s", transactionID, queryID, message)); } public static void main(String[] args) { try { throw new DatabaseException(3, 7, "Write failed");//抛出一个编译时异常 } catch (DatabaseException e) { System.out.println(e);//打印异常对象 } } } /** com.yue.string.DatabaseException: (t3, q7) Write failed */