zoukankan      html  css  js  c++  java
  • 《Thinking In Java》笔记之十三章 字符串

    #Thinking In Java# Chapter13 String

    不可变String

    String类中看起来会修改String的方法,实际上均为创建了一个全新的对象,而最初的对象丝毫未动,对方法传递字符串,实际传递的是引用的一个拷贝,而该引用所指的对象一直待在原物理位置上,从未动过。

    重载“+”与String builder

    package strings;
    
    public class WhitherStringBuilder {
        public String implicit(String[] fields) {
            String result = "";
            for (int i = 0; i < fields.length; i++) {
                result += fields[i];
            }
            return result;
        }
        public String explicit(String[] fields) {
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < fields.length; i++) {
                result.append(fields[i]);
            }
            return result.toString();
        }
    }
    
    

    方法一使用了多个String对象,方法二使用了StringBuilder,运行javap -c WitherStringBuilder查看字节码。可知对于implicit方法,在每一次循环内部都生成了一个新的StringBuilder对象。而explicit方法循环部分代码更简短,只生成一个StringBuilder对象,显示地创建还可以预先为StringBuilder指定大小,如果已经知道最后的字符串大小大概有多长,可以预先指定大小避免多次重新分配缓存。

    因此如果为一个类编写toString()方法时,如果字符串操作比较简单,可以信赖编译器。但如果药在toString方法中使用循环,最好自己创建一个StringBuilder对象,用它来构造最终的结果。

    参考示例

    package strings;
    
    import java.util.Random;
    
    public class UsingStringBuilder {
        public static Random rand = new Random(47);
        public String toString() {
            StringBuilder result = new StringBuilder("[");
            for (int i = 0; i < 25; i++) {
                result.append(rand.nextInt(100));
                result.append(",");
            }
            result.delete(result.length() - 1, result.length());
            result.append("]");
            return result.toString();
        }
    
        public static void main(String[] args) {
            UsingStringBuilder usb = new UsingStringBuilder();
            System.out.println(usb);
        }
    }
    
    /*
    [58,55,93,61,61,29,68,0,22,7,88,28,51,89,9,78,98,61,20,58,16,40,11,22,4]
    */
    

    StringBuilder提供了丰富全面的方法,包括insert(),replace(),substring()甚至reverse(),但最常用的还是append()和toString(),还有delete()方法,StringBuilder是Java SE5引入的,在这之前用的是StringBuffer。后者是线程安全,开销大,所以在Java SE 5/6,字符串操作应该更快。

    无意识的递归之toString()方法

    如果你希望 toString()方法打印出对象的内存地址,也许你会考虑使用this关键字

    package strings;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class InfiniteRecursion {
        @Override
        public String toString() {
            return "InfiniteRecursion address: " + this + "
    ";
            //此处使用this造成的递归,你看出来了吗?( ̄▽ ̄)"
        }
    
        public static void main(String[] args) {
            List<InfiniteRecursion> v = new ArrayList<InfiniteRecursion>();
            for (int i = 0; i < 10; i++) {
                v.add(new InfiniteRecursion());
            }
            System.out.println(v);
        }
    
    }
    
    

    然而在打印时却会一串非常长的异常,这里发生了自动类型转换,由InfiniteRecursion类型转换成String类型。因为编译器看到一个String对象后接着一个“+”,而后面的对象并不是String,再次发生类型转换,调用this的toString()方法,发生递归调用。

    如果真的想要打印出地址,应该调用Object.toString(),所以应该调用super.toString()方法,而不是thos

    String上的操作

    以下是String对象具备的一些基本方法。重载的方法归纳在同一行中:

    构造器 参数,重载版本 应用
    length() String中字符个数
    charAt() Int索引 取得String该索引位置上的char
    getChars(),getBytes() 要复制部分起点和终点的索引,要复制的目标数组,目标数组的起始索引 复制char或byte到一个目标数组里
    toCharArray() 生成一个char[],包含String的所有字符
    equals(), equalsIgnoreCase() 与之进行比较的String 比较两个String的内容是否相同
    compareTo() 与之进行比较的String 按词典顺序比较String的内容,比较结果为正数,负数,零
    contains() 要搜索的CharSequence 如果String对象包含参数的内容,则返回true
    contentEquals() 与之比较的CharSequence或StringBuffer 如果该String与参数的内容完全一致
    equalsIgnoreCase() 与之进行比较的String 忽略大小写,如果两个String内容相同,返回true
    regionMatcher() 该索引的偏移量,另一个String及其索引偏移量,要比较的长度。重载版本增加了“忽略大小写功能” 返回boolean结果,以表明所比较区域是否相等
    startsWith 可能的起始String。重载版本在参数中增加了偏移量 返回boolean结果,以表明该String是否以此参数开始
    endsWith 该String可能的后缀String 返回boolean结果,以表明此参数是否是该字符串的后缀
    indexOf(),lastIndexOf() 重载版本包括:char, char与起始索引,String, String与起始索引 如果该String不包含该参数,就返回-1;否则返回该参数在String中。LastIndexOf()是从后往前搜索
    substring()(subSequence()) 重载版本:起始索引;起始索引+终点坐标 返回一个新的String以包含指定的子字符串
    concat() 要连接的String 返回一个新的String对象,内容为原始String连接上参数String
    replace() 要替换掉的字符,用来进行替换的新字符。也可以用一个CharSequence来替换另一个CharSequence 返回替换字符后的新String对象,如果没有替换发生,则返回原来的String对象
    toLowerCase() toUpperCase() 将字符的大小写改变后,返回一个新String对象。如果没有发生改变,则返回原始的String对象
    trim() 将String两端的空白字符删除后,返回一个新的String对象。如果没有发生改变,则返回原始的String对象
    valueOf() Object; char[];char[], 偏移量,与字符个数;boolean; char; int; long; float; double 将基本数据型态转换成 String
    intern() 为每个唯一的字符序列生成一个且仅生成一个String引用

    格式化输出

    System.out.format()

    package strings;
    
    public class SimpleFormat {
        public static void main(String[] args) {
            int x = 5;
            double y = 3.1415926;
            System.out.println("Row 1 : [" + x + "" + y + "]");
            //new ways
            System.out.format("Row 1: [%d %f]
    ", x, y);
            System.out.printf("Row 1: [%d %f]
    ", x, y);
        }
    }
    /* 
    Row 1 : 53.1415926]
    Row 1: [5 3.141593]
    Row 1: [5 3.141593]
    */
    

    Formatter类

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

    package strings;
    
    import javax.swing.plaf.synth.SynthEditorPaneUI;
    import java.io.PrintStream;
    import java.util.Formatter;
    
    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)
    ", name, x, y);
        }
    
        public static void main(String[] args) {
            PrintStream outAlias = System.out;
            Turtle tom = new Turtle("Tom", new Formatter(System.out));
            Turtle jerry = new Turtle("Jerry", new Formatter(outAlias));
            tom.move(0, 0);
            jerry.move(4, 8);
            tom.move(3, 4);
            jerry.move(2, 5);
            tom.move(3, 3);;
            jerry.move(3, 3);
        }
    }
    /* 
    tommy The turtle is at (0 , 0)
    Turtle The turtle is at (4 , 8)
    tommy The turtle is at (3 , 4)
    Turtle The turtle is at (2 , 5)
    tommy The turtle is at (3 , 3)
    Turtle The turtle is at (3 , 3)
    */
    

    所有的tom都会被输入得到System.out中,而所有的jerry都会输出到System.out的别名上。Formatter的构造器重载后可以接收多种输出目的地,包括PrintStream(),OutputStream和File

    格式化说明符

    抽象语法:%[argument_index$] [flags] [width] [.precision] conversion

    默认情况下,数据是右对齐,可以使用“-”左对齐

    width:指明最大尺寸,可以应用于各种类型的数据转换,并且行为方式都一样

    precision: 不是所有类型的数据都能使用precision,而且应用于不同的类型数据含义不同,在将precision应用于String时,表示String可输出字符的最大数量(注意体会与width的区别,width强调的是尺寸),而precision应用于浮点数表示小数部分要显示出来的位数。小数位数过多舍入,太少尾部补0。precision不能应用于整数,因为整数没有小数部分。

    package strings;
    
    import java.util.Formatter;
    
    public class Receipt {
        private double total = 0;
        private Formatter f = new Formatter(System.out);
        public void printTitle() {
            f.format("%-15s %5s %10s
    ", "Item", "Qty", "Price");
            f.format("%-15s %5s %10s
    ","----", "---", "-----");
        }
        public void print(String name, int qty, double price) {
            f.format("%-15.15s %5d %10.2f
    ",name, qty, price);
            //%-15.15s,-表示左对齐,前15表示width,后15表示precision
            total += price;
        }
        public void printTotal() {
            f.format("%-15s %5s %10.2f
    ", "Tax", "", total * 0.06);
            f.format("%-15s %5s %10s
    ", "", "", "-----");
            f.format("%-15s %5s %10.2f
    ", "Total", "", total * 1.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.25
    Princess Peas       3       5.10
    Three Bears Por     1      14.29
    Tax                         1.42
                               -----
    Total                      25.06
    */
    

    Formatter转换

    类型转换字符

    字符 含义
    d 整数型
    c Unicode
    b boolean
    s String
    f 浮点值
    e 浮点数(科学计数)
    x 整数(十六进制)
    h 散列码(十六进制)
    % 字符“%”

    荔枝:

    Formatter f = new Formatter();
    int i = 0;
    f.format("b : %b", i);
    /* 
    b : true
    */
    

    b转换:对于boolean基本类型或者Boolean对象,其转换类型永远是true或false,对其他类型的参数,只要该参数不为null,那么转换的结果永远为true,即使是数字0,这与C等其他语言中不一样,需要小心。

    便捷的String.format()

    String.format()是一个static方法,它接受与Formatter.format()方法一样的参数,但是,但是,但是,它返回一个String对象。当你只需format()方法一次的时候,String.format()使用起来非常方便。其实在String.format()内部它也创建了一个Formatter对象,然后将参数传入Formatter。但不如使用便捷的String.format()方法并且使代码更具有可读性。

    package strings;
    
    public class DatabaseException extends Exception {
        public DatabaseException(int transactionID, int queryId, String message) {
            super(String.format("(transactionID%d, queryId%d) %s", transactionID, queryId, message));
        }
    
        public static void main(String[] args) {
            try {
                throw new DatabaseException(3, 7, "write failed");
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
    
    

    dump工具(十六进制转换)

    package strings;
    
    import java.io.*;
    
    public class Hexadecimal {
    
        public static String format(byte[] data) {
            StringBuilder result = new StringBuilder();
    
            int n = 0;
            for (byte b : data) {
                if (n % 16 == 0)
                    result.append(String.format("%05X: ", n)); // 占用5个位置(16进制表示)
                result.append(String.format("%02X ", b)); // 占用2个位置(16进制表示)
                n++;
                if (n % 16 == 0)
                    result.append("
    ");
            }
            result.append("
    ");
            return result.toString();
        }
    
        public static void main(String[] args) throws Exception {
            if (args.length == 0)
                System.out.println(format(BinaryFile.read("E:/ThinkingInJava/src/strings/Hexadecimal.java")));
            else
                System.out.println(format(BinaryFile.read(new File(args[0]))));
        }
    
    }
    
    /* 
    00010: 0D 0A 0D 0A 69 6D 70 6F 72 74 20 6A 61 76 61 2E 
    00020: 69 6F 2E 2A 3B 0D 0A 0D 0A 70 75 62 6C 69 63 20 
    ...
    */
    
    package strings;
    
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    
    public class BinaryFile {
        public static byte[] read(File bFile) throws IOException {
            BufferedInputStream bf = new BufferedInputStream(new FileInputStream(
                    bFile));
            try {
                byte[] data = new byte[bf.available()];
                bf.read(data);
                return data;
            } finally {
                bf.close();
            }
        }
    
        public static byte[] read(String bFile) throws IOException {
            return read(new File(bFile).getAbsoluteFile());
        }
    }
    
    
  • 相关阅读:
    CodeIgniter框架对数据库查询结果进行统计
    PHP的内存回收(GC)
    使用ajax请求后端程序时,关于目标程序路径问题
    JavaScript中的各种X,Y,Width,Height
    Qt编写气体安全管理系统7-设备监控
    Qt编写气体安全管理系统6-地图监控
    Qt编写气体安全管理系统5-数据监控
    Qt编写气体安全管理系统4-通信协议
    Qt编写气体安全管理系统3-用户模块
    Qt编写气体安全管理系统2-界面框架
  • 原文地址:https://www.cnblogs.com/Glov/p/13512872.html
Copyright © 2011-2022 走看看