Java8源码 java.lang 包 02、String类

Java类库复习——java.lang.String NULL 博文链接:https://xiaoyaoeric.iteye.com/blog/1494989 立即下载

1、类的定义

String 类

public final class String implements Serializable, Comparable<String>, CharSequence 

String 类被声明为 final 的,意味着它不可以被继承。

  • 实现了 Serializable 接口使它可以被序列化
  • 实现了 Comparable 接口便于字符串之前的比较
  • 实现了 CharSequence 接口,该接口是 char 值的一个可读序列,代表一个有序字符集合。

CharSequence 接口

CharSequence 接口中声明了如下几个方法:

    public interface CharSequence {
        // 获取字符序列长度
        int length();
        // 获取某个指定位置的字符
        char charAt(int index);
        // 获取子序列
        CharSequence subSequence(int start, int end);
        // 将字符序列转换为字符串
        public String toString();
    }

对于一个抽象类或者是接口类,不能使用 new 来进行赋值,但是可以通过以下的方式来进行实例的创建:

CharSequence cs = "hello"; 	// 等价于 CharSequence cs = new String("hello");

CharSequence 就是字符序列,而 String 本质上是通过字符数组实现的!
CharSequence 和 String 都能用于定义字符串,但 CharSequence 的值是可读可写序列,而 String 的值是只读序列。

2、成员变量

    // 字符数组,用于存储字符串中的字符 
    private final char[] value;
    // 缓存字符串的hash值,默认是0
    private int hash;
  • final 修饰 value,表示其一旦被赋值,内存地址是绝对无法修改的
  • private 修饰 value,外部绝对访问不到,且 String 类也没用提供任何访问方法

String 是不可变类。即类值一旦被初始化,就不能再被改变了,如果被修改,将会是新的类。也就是说 String s = “a”; s = “b” 时并不是对变量 s 进行了修改,而是重新指向了新的字符串对象。

String 为什么要设计成不可变类呢?

  • 不可变对象不能被写,避免了引用传值,所以线程安全。
  • 方便使用字符串常量池,节省开销

不可变类的设计通常要遵循以下几个原则:

  1. 将类声明为 final,所以它不能被继承。
  2. 将所有的成员声明为私有的,这样就不允许直接访问这些成员。
  3. 对变量不要提供 setter 方法。
  4. 将所有可变的成员声明为 final,这样只能对它们赋值一次。
  5. 通过构造器初始化所有成员,进行深拷贝(deep copy)。
  6. 在 getter 方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝。

常量池

1)class 文件中的常量池

在 class 文件中,除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种字面量和符号引用。

编写如下代码,并查看其 class 文件内容:

    public class Person {
        final int age = 10;
        String name = "老王";
    }

常量池
在上图中可以看到,字面量 10 和 “老王” 出现在 Constant pool 列表中。

字面量是用于表达源代码中一个固定值的表示法。数字,字符串等都有字面量表示。

2)运行时常量池

根据《java虚拟机规范》的规定,class 文件的常量池中的信息,将在类加载后进入方法区中的常量池存储。

3、构造方法

1)利用字节数组来生成字符串

byte 是网络传输或存储的序列化形式。在很多传输和存储的过程中需要将 byte[] 数组和 String 进行相互转化。

不带编码

  • String(byte[] bytes)

        public String(byte[] bytes) {
            this(bytes, 0, bytes.length);
        }
    
  • String (byte[] bytes, int offset, int length)

        public String(byte[] bytes, int offset, int length) {
            checkBounds(bytes, offset, length);
            // 将字节数组解码为字符数组
            this.value = StringCoding.decode(bytes, offset, length);
        }
    
        private static void checkBounds(byte[] bytes, int offset, int length) {
            if (length < 0)
                throw new StringIndexOutOfBoundsException(length);
            if (offset < 0)
                throw new StringIndexOutOfBoundsException(offset);
            if (offset + length > bytes.length)
                throw new StringIndexOutOfBoundsException(offset + length);
        }
    

    如果没有指明解码使用的字符集,StringCoding 的 decode方法首先调用系统的默认编码格式;
    如果没有指定编码格式(charsetName),则默认使用 ISO-8859-1编码格式进行编码操作。

带编码

字符集(charset)是一个系统支持的所有抽象字符的集合。常见的字符集有 ascii字符集、Unicode字符集。

字符编码(charsetName)是对字符集的一套编码规则,将具体的字符进行“数字化”,便于计算机理解和处理。例如常用的 UTF-8 字符编码就是对 Unicode 字符集的一种具体编码规范。

  • String (byte[] bytes, Charset charset)
  • String (byte[] bytes, int offset, int length, Charset charset)
  • String (byte[] bytes, String charsetName)
  • String (byte[] bytes, int offset, int length, String charsetName)
    public String(byte[] bytes, int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }

其他逻辑不变,参数从 Charset 换成 String,即为后两种的实现

可以看到利用字节数组的核心逻辑是:

    char[] value = StringCoding.decode(charsetName, bytes, offset, length);
    char[] value = StringCoding.decode(charset, bytes, offset, length);
    char[] value = StringCoding.decode(bytes, offset, length);

StringCoding 类的 decode 方法如下:

    private final static ThreadLocal<SoftReference<StringDecoder>> decoder
            = new ThreadLocal<>();

    static char[] decode(String charsetName, byte[] ba, int off, int len)
            throws UnsupportedEncodingException {
      // 从线程级缓存中获取反序列化器
        StringDecoder sd = deref(decoder);
        String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
   	  // 缓存中没有反序列化器,或者虽然有,但是之前反序列化的字符集与这次不同,则重新生成decoder
        if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
                              || csn.equals(sd.charsetName()))) {
            sd = null;
            try {
                Charset cs = lookupCharset(csn);
                if (cs != null)
                    sd = new StringDecoder(cs, csn);
            } catch (IllegalCharsetNameException x) {}
            if (sd == null)
                throw new UnsupportedEncodingException(csn);
            // 将decoder放入线程级缓存,以备下次使用
            set(decoder, sd);
        }
        // 完成反序列化
        return sd.decode(ba, off, len);
    }

    // 从缓存中获取反序列器,此处使用了软引用,便于jvm在内存不足时,释放该缓存
    private static <T> T deref(ThreadLocal<SoftReference<T>> tl) {
        SoftReference<T> sr = tl.get();
        if (sr == null)
            return null;
        return sr.get();
    }

    // 判断字符集是否支持,并加载字符集处理类
    private static Charset lookupCharset(String csn) {
        if (Charset.isSupported(csn)) {
            try {
                return Charset.forName(csn);
            } catch (UnsupportedCharsetException x) {
                throw new Error(x);
            }
        }
        return null;
    }

    // 将对象的软引用放入线程级缓存
    private static void set(ThreadLocal tl, Object ob) {
        tl.set(new SoftReference(ob));
    }

就是利用了线程级缓存来缓存 decoder,这样就不必每次都实例化新的 decoder,同时线程级缓存也确保了反序列化的操作是线程安全的。其中 ThreadLocal 和 SoftReference 结合的用法值得借鉴。

2)利用字符数组来生成字符串

  • String(char[] value)

        public String(char[] value) {
            this.value = Arrays.copyOf(value, value.length);
        }
    
  • String(char[] value, int offset, int count)

        public String(char[] value, int offset, int count) {
            if (offset < 0) {
                throw new StringIndexOutOfBoundsException(offset);
            }
            if (count <= 0) {
                if (count < 0) {
                    throw new StringIndexOutOfBoundsException(count);
                }
                if (offset <= value.length) {
                    this.value = "".value;
                    return;
                }
            }
            // Note: offset or count might be near -1>>>1.
            if (offset + count > value.length) {
                throw new StringIndexOutOfBoundsException(offset + count);
            }
            this.value = Arrays.copyOfRange(value, offset, offset+count);
        }
    

3)利用字符串来创建一个新的字符串

  • String(String original)

        public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
        }
    

因为 String 一旦定义之后是不可以改变的,不用担心改变源 String 的值会影响到目标 String 的值。
所以可以直接将源 String 中的 value 和 hash 两个属性直接赋值给目标 String。

4)利用 StringBuffer/StringBuilder 来创建一个新的字符串

  • String(StringBuffer buffer)

  • String(StringBuilder builder)

        public String(StringBuffer buffer) {
            synchronized(buffer) {
                this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
            }
        }
    

参数从 StringBuilder 变为 StringBuilder 即为后者的实现

这两种方法都较少用,一般都是通过它们的 toString 方法来获得字符串对象。

5)保护型构造方法

    String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = value;
    }

这是 java7 后加入的新特性。从代码中可以看出,该方法和 String(char[] value) 有两点区别:

  1. 该方法多了一个 boolean 类型的参数 share。其实这个参数在方法体中根本没被使用,也给了注释,目前不支持使用 false,只使用 true。那么可以断定,加入这个 share 的只是为了区分于 String(char[] value) 方法,构成方法重载。
  2. 具体的方法实现不同。String(char[] value) 方法在创建 String 的时候会用到会用到 Arrays 的 copyOf 方法将 value 中的内容逐一复制到 String 当中,而这个 String(char[] value, boolean share) 方法则是直接将 value的引用赋值给 String 的 value。也就是说,这个方法构造出来的 String 和参数传过来的 char[] value 共享同一个数组。

那么,为什么 java 会提供这样一个方法呢?

  • 性能好。一个是直接给数组赋值(相当于直接将 String 的 value 的指针指向 char[] 数组),一个是逐一拷贝。显然直接赋值更快。
  • 节约内存。共享内部数组所以节约内存
  • 安全。该方法之所以设置为默认的访问权限,是因为一旦该方法设置为 public,在外面可以访问的话,如果构造方法没有对 arr 进行拷贝,那么其他人就可以在字符串外部修改该数组,由于它们引用的是同一个数组,因此对 arr 的修改就相当于修改了字符串,那就破坏了字符串的不可变性。甚至会导致内存泄漏(内存泄漏是指不用的内存无法被释放)。

4、其他方法

对于数组的操作,必定需要使用 offset(初始偏移量)和 count( 长度)来进行标记和记录。
String 类的大多数方法都是围绕着 value、offset、count 和 字符编码 来展开的。

    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

实现 CharSequence 的方法

  • charAt(int index):返回指定索引处的 char 值

    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }
    
  • length():返回此字符串的长度

    public int length() {
        return value.length;
    }
    
  • subSequence(int beginIndex, int endIndex):返回一个新的字符序列,它是此序列的一个子序列

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);
    }
    

使用 String(value, beginIndex, subLen) 方法创建一个新的 String 并返回,这个方法会将原来的 char[] 中的值逐一复制到新的 String 中,两个数组并不是共享的,虽然这样做损失一些性能,但是有效地避免了内存泄漏。

重写 Object 的方法

equals()

对于字符串来说,往往是希望知道它们在逻辑上是否相等,而不是想了解它们是否指向同一个对象.

    @Override
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

可以看到,代码逻辑是先宏观比较,再微观比较,这种写法很大程度上提高了比较的效率,值得学习。

hashcode()

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

hashCode 的实现其实就是使用数学公式:

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

在这段代码中,值得注意的是:

1、将整数值与char值相加会得到什么?

根据 java 基本类型间的强制转换规则,char 型将会被转换为 int 型,然后与 int 类型的值相加。那么 char 在转换为 int 时该如何取值呢?其实这里利用了码位(code point)的概念,通过如下的程序来看一下:

    String str = "abc123中国";
    for (int i = 0; i < str.length(); i++) {
        System.out.println((int)str.charAt(i) + "," + str.codePointAt(i));
    }

运行结果:

97,97
98,98
99,99
49,49
50,50
51,51
20013,20013
22269,22269

由此可知,char 字符转换为整型时,其值为其在 Unicode 字符集中的码位。

码位(code point):码位是表示一个字符在码空间中的数值。可以对具体的字符集进行字符编码。
例如:ascii 包含 128个 码位(范围是 0-127),数字 0 的码位是 48。

2、31是怎么来的?

在 Java Effective 中有提及:

之所以选择 31,是因为它是一个奇素数(质数)。如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为与 2 相乘等价于移位运算。使用素数的好处并不明显,但是习惯上都使用素数来计算散列结果。31 有个很好的特性。即用移位和减法来代替乘法,可以得到更好的性能:31 * i == (i << 5) - i。现代的 VM 可以自动完成这种优化。

3、内容相同的两个字符串的哈希值是否一定相等?
    int a =  "Aa".hashCode();	// 2112
    int b =  "BB".hashCode();	// 2112

不同的输入得到了相同的哈希值,这叫 哈希碰撞或哈希冲突。所以:

  • 哈希值相同的字符串,字面值不一定相同
  • 字面值相同的字符串,哈希值一定相同
  • 字面值相同的字符串,内存地址不一定相同

在存储数据计算 hash 地址的时候,通常希望尽量减少 hash 冲突。如果使用相同 hash 地址的数据过多,那么这些数据所组成的 hash 链就很长(HashMap采用了链地址法,即数组+链表的数据结构),从而降低了查询效率!所以在选择系数的时候要选择尽量长的系数并且让乘法尽量不要溢出的系数,因为如果计算出来的 hash 地址越大,所谓的 “冲突” 就越少,查找起来效率也会提高。

toString()

    //	返回此对象本身(它已经是一个字符串!)
    public String toString() {
        return this;
    }

比较

  • compareTo():来源 Comparable 接口。按字典顺序比较两个字符串

     public int compareTo(String anotherString) {
         int len1 = value.length;
         int len2 = anotherString.value.length;
         int lim = Math.min(len1, len2);
         char v1[] = value;
         char v2[] = anotherString.value;
    
         int k = 0;
         while (k < lim) {
             char c1 = v1[k];
             char c2 = v2[k];
             if (c1 != c2) {
                 return c1 - c2;
             }
             k++;
         }
         return len1 - len2;
     }
    
  • compareToIgnoreCase(String str):按字典顺序比较两个字符串,不考虑大小写

    	public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                             = new CaseInsensitiveComparator();
      	public int compareToIgnoreCase(String str) {
            return CASE_INSENSITIVE_ORDER.compare(this, str);
        }
    

    String 类的 compareToIgnoreCase 方法实际上是调用了 其静态内部类 CaseInsensitiveComparator 的 compare 方法。

     private static class CaseInsensitiveComparator
                implements Comparator<String>, java.io.Serializable {
            // use serialVersionUID from JDK 1.2.2 for interoperability
            private static final long serialVersionUID = 8575799808933029326L;
    
            public int compare(String s1, String s2) {
                int n1 = s1.length();
                int n2 = s2.length();
                int min = Math.min(n1, n2);
                for (int i = 0; i < min; i++) {
                    char c1 = s1.charAt(i);
                    char c2 = s2.charAt(i);
                    if (c1 != c2) {
                        c1 = Character.toUpperCase(c1);
                        c2 = Character.toUpperCase(c2);
                        if (c1 != c2) {
                            c1 = Character.toLowerCase(c1);
                            c2 = Character.toLowerCase(c2);
                            if (c1 != c2) {
                                // No overflow because of numeric promotion
                                return c1 - c2;
                            }
                        }
                    }
                }
                return n1 - n2;
            }
    
            /** Replaces the de-serialized object. */
            private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
        }
    

    可以看到,它只是在 String 类的 compare 方法上多了一层大小写转换操作而已。

  • contentEquals(CharSequence cs) 和 contentEquals(StringBuffer sb)

      public boolean contentEquals(CharSequence cs) {
          // Argument is a StringBuffer, StringBuilder
          if (cs instanceof AbstractStringBuilder) {
              if (cs instanceof StringBuffer) {
                  synchronized(cs) {
                      return nonSyncContentEquals((AbstractStringBuilder)cs);
                  }
              } else {
                  return nonSyncContentEquals((AbstractStringBuilder)cs);
              }
          }
          // Argument is a String
          if (cs instanceof String) {
              return equals(cs);
          }
          // Argument is a generic CharSequence
          char v1[] = value;
          int n = v1.length;
          if (n != cs.length()) {
              return false;
          }
          for (int i = 0; i < n; i++) {
              if (v1[i] != cs.charAt(i)) {
                  return false;
              }
          }
          return true;
      }
    
      private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
          char v1[] = value;
          char v2[] = sb.getValue();
          int n = v1.length;
          if (n != sb.length()) {
              return false;
          }
          for (int i = 0; i < n; i++) {
              if (v1[i] != v2[i]) {
                  return false;
              }
          }
          return true;
      }
    
  1. 如果是 StringBuffer或StringBuilder。先判断长度是否一样长,再判断内容是否一致。
  2. 如果是 String 类,则调用其内部是 equals 方法。
  3. 如果是其他 CharSequence 子类,判断逻辑同第1点。
    public void stringTest() {
        String str = "hello";
        System.out.println(str.contentEquals("hello")); //true
        System.out.println(str.contentEquals(new StringBuffer("hello"))); //true
    }
    

字符串乱码(getBytes)

  • byte[] getBytes() :使用平台默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组
  • byte[] getBytes(Charset charset):使用给定的 charset 将此 String 编码到 byte 序列
  • byte[] getBytes(String charsetName):使用指定的字符集将此 String 编码为 byte 序列
    String str  ="nihao 你好";
    byte[] bytes = str.getBytes("ISO-8859-1");
    System.out.println(new String(bytes));  //  nihao ??

把代码修改成 System.out.println(new String(bytes,"ISO-8859-1")); 是否就可以了?这是不行的。

乱码的问题的根源主要是两个:字符集不支持复杂汉字、二进制进行转化时字符集不匹配。
对于 String 来说,getBytes 和 new String 两个方法都会使用到编码,ISO-8859-1 不支持汉字,把这两处的编码替换成 UTF-8 后,打印出的结果就正常了。

这系列方法的底层核心实现是字节数组带编码的核心实现形式

连接(concat)

concat 源码

    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

    void getChars(char dst[], int dstBegin) {
        //  byte[] src = new byte[]{2,4,0,0,0,0,0,10,15,50}
        //  byte[] dest = new byte[5];
        //	System.arrayCopy(src,0,dest ,0,5);//src=[2,4,0,0,0]
        System.arraycopy(value, 0, dst, dstBegin, value.length);
    }

数组拷贝

System.arraycopy
    int[] arr = {1,2,3,4,5};
    int[] copied = new int[10];
    System.arraycopy(arr, 0, copied, 1, 5);
    System.out.println(Arrays.toString(copied));

运行结果

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 2, 3, 4, 5, 0, 0, 0, 0]
Arrays.copyOf
    int[] arr = {1,2,3,4,5};
    int[] copied = Arrays.copyOf(arr, 10);
    System.out.println(Arrays.toString(copied));
    copied = Arrays.copyOf(arr, 3);
    System.out.println(Arrays.toString(copied));

运行结果

[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
[1, 2, 3]

两者的区别在于,Arrays.copyOf() 不仅仅只是拷贝数组中的元素,在拷贝元素时,会创建一个新的数组对象。而 System.arrayCopy 只拷贝已经存在数组元素。

实际上 Arrays.copyOf() 的底层还是调用了 System.arrayCopyOf() 方法:

    public static int[] copyOf(int[] original, int newLength) { 
       int[] copy = new int[newLength]; 
       System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); 
       return copy; 
    }

查找(indexOf)

    // 返回指定子字符串中第一次出现str的索引
    public int indexOf(String str) {
        return indexOf(str, 0);
    }

    //	返回指定字符串中第一次出现str的索引,从fromIndex开始
    public int indexOf(String str, int fromIndex) {
       return indexOf(value, 0, value.length,
                      str.value, 0, str.value.length, fromIndex);
    }

    static int indexOf(char[] source, int sourceOffset, int sourceCount,
                char[] target, int targetOffset, int targetCount,int fromIndex) {
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (targetCount == 0) {
            return fromIndex;
        }

        char first = target[targetOffset];
        int max = sourceOffset + (sourceCount - targetCount);

        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            //取到第一个字符相同的位置
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }

            //循环比较接下来所有所有的字符,如果全部相同则返回第一个字符的位置
            if (i <= max) {
                int j = i + 1;
                int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j]
                     == target[k]; j++, k++);

                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    }

这个方法首先会查找子字符串的头字符在此字符串中第一次出现的位置,再以此位置的下一个位置开始,然后将子字符串的字符依次和此字符串中字符进行比较,如果全部相等,则返回这个头字符在此字符串中的位置;如果有不相等的,则继续在剩下的字符串中查找,继续进行上面的过程,直到查找到子字符串或没有找到返回-1为止。

该用 static 修饰是因为 AbstractStringBuilder 类的indexOf(String str, int fromIndex) 会调用这个方法,为了方便其他类调用该方法所以用 static 修饰。

合并(join)

    String.join("-", "Java", null, "cool"); //Java-null-cool
    String.join(",", "a").join(",", "b");	//b

此方法是静态方法。方法有两个入参,参数一是合并的分隔符,参数二是合并的数据源,数据源支持数组和 List,在使用的时候,有两个不太方便的地方:

  1. 不支持依次 join 多个字符串,比如第二例中第一次 join 的值被第二次 join 覆盖了;
  2. 如果 join 的是一个 List,无法自动过滤掉 null 值

可以使用 Guava 的 API 来解决上述问题:

    // 依次 join 多个字符串,Joiner 是 Guava 提供的 API
    Joiner joiner = Joiner.on(",").skipNulls();
    String result = joiner.join("hello",null,"china");
    log.info("依次 join 多个字符串:{}",result);

    List<String> list = Lists.newArrayList(new String[]{"hello","china",null});
    log.info("自动删除 list 中空值:{}",joiner.join(list));

    // 输出的结果为;
    // 依次 join 多个字符串:hello,china
    // 自动删除 list 中空值:hello,china

底层调用的是 AbstractStringBuilder类的 append 方法。

切割(split)

    String s =":boo:and:foo:";
    s.split(":") 结果:["", "boo", "and", "foo", ""]
    // 第二个参数limit,这个数值n如果 >0 则会执行切割 n-1 次,数组长度不会大于切割次数
    s.split(":", 2) 结果:["", "boo:and:foo:"]
    s.split(":", 5) 结果:["", "boo", "and", "foo", ""]
    // 如果参数limit是非正数,则执行切割到无限次,数组长度也可以是任何数值,保留空字符串
    s.split(":", -2) 结果:["", "boo", "and", "foo", ""]
    // 如果参数limit等于0,则会执行切割无限次并且去掉该数组最后的所有空字符串
    s.split(":", 0) 结果:["", "boo", "and", "foo"]
    // 默认第二个参数为0
    s.split("o") 结果:[":b", "", ":and:f", " ", ":"]
    s.split("o",2) 结果:[":b", "o:and:foo:"]

从拆分结果中可以看到,空值是拆分不掉的,仍然成为结果数组的一员,如果想删除空值,只能自己拿到结果后再做操作,但 Guava(Google 开源的技术工具) 提供了一些可靠的工具类,可以快速去掉空值,如下:

    String a =",a, ,  b  c ,";
    // Splitter 是 Guava 提供的 API 
    List<String> list = Splitter.on(',')
        .trimResults()// 去掉空格
        .omitEmptyStrings()// 去掉空值
        .splitToList(a);
    log.info("Guava 去掉空格的分割方法:{}",JSON.toJSONString(list));	// ["a","b  c"]

替换(replace)

    public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }
  1. 先判断替换字符和被替换字符是否相同,相同则返回源数据,否则往下进行
  2. 查询原数组中是否存在要替换的字符,如果没有则返回源数据,否则往下进行
  3. 新建一个和原长度一样的数组,将第一个需要被替换的字符之前的元素存放进来
  4. 从第一个要被替换的字符开始,判断其是否为要替换的字符,如果是则用新值替换,否则保持原样
    public void testReplace() {
      String str ="hello word !!";
      log.info("替换之前 :{}",str);

      // 替换全部字符串或字符
      str = str.replace('l','d');
      log.info("替换所有字符 :{}",str);

      // 基于正则表达式替换全部字符串或字符
      str = str.replaceAll("d","l");
      log.info("替换全部 :{}",str);

      // 基于正则表达式只替换第一次出现的字符串或字符
      str = str.replaceFirst("l","");
      log.info("替换第一个 l :{}",str);
    }    

输出的结果是:

替换之前 :hello word !!
替换所有字符 :heddo word !!
替换全部 :hello worl !!
替换第一个 :helo worl !!

String 类中的方法还有很多,比如获取子字符串的 substring 方法等等。其原理不外乎围绕着字符数组 value、下标 offset、字符串长度 count 这几个变量来展开,万变不离其宗。

转换(copyValueOf 与 valueOf)

valueOf

将变量转换成 String 类型。

Object对象
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }
    
    public static String valueOf(char data[]) {
        return new String(data);
    }
    
    public static String valueOf(char data[], int offset, int count) {
        return new String(data, offset, count);
    }
基本类型
    public static String valueOf(boolean b) {
          return b ? "true" : "false";
    }

    public static String valueOf(char c) {
           char data[] = {c};
           return new String(data, true);
    }
    public static String valueOf(int i) {
          return Integer.toString(i);
    }

    public static String valueOf(long l) {
         return Long.toString(l);
    }

    public static String valueOf(float f) {
         return Float.toString(f);
    }

    public static String valueOf(double d) {
        return Double.toString(d);
    }

copyValueOf

    public static String copyValueOf(char data[], int offset, int count) {
        return new String(data, offset, count);
    }


    public static String copyValueOf(char data[]) {
        return new String(data);
    }

可以看到这两个函数是等同于

    String valueOf(char data[], int offset, int count);
    String valueOf(char data[]);

这两个函数的,那么当初为什么要设计这样两个copy函数呢?

这是因为在早期 String 构造器在实现上是不会拷贝数组的,而是直接将参数的 char[] 数组作为 String 的 value 属性。所以执行 test[0] = ‘A’; 时会导致早期的字符串发生变化。为了避免这个问题,提供了 copyValueOf 方法,每次都拷贝成新的字符数组来构造新的 String 对象。但是现在的 String 对象,在构造器中就通过拷贝新数组实现了,所以这两个方面在本质上已经没区别了。

字符数组的内容会被拷贝,字符数组中的子串的修改将不会影响返回的字符串。
所以以下情况修改 data 并不会影响 s1 与 s2 的值:

    char[] data = "123456789";
    String s1 = String.valueOf(data); 	   // s1 = "123456789"
    String s2 = String.copyValueOf(data); // s2 = "123456789"

    data[0] = '9';
    System.out.println(s1);	// s1 = "123456789"
    System.out.println(s1); // s2 = "123456789"

intern()

    public native String intern();

该方法返回一个字符串对象的内部化引用。String 类维护了一个初始为空的字符串的对象池,当 intern 方法被调用时,如果对象池中已经包含这一个相等的字符串对象则返回对象池中的实例,否则添加字符串到对象池并返回该字符串的引用。该方法开发中很少用到。

5、其他

反射修改 String

String 在通常意义上虽然被认为是”不可变“的,但是仍然可以利用反射来改变 String 的值,如下:

    String java = "java";
    System.out.println("old value:" + java);
    try {
        Field field = java.getClass().getDeclaredField("value");
        field.setAccessible(true);
        char[] value = (char[]) field.get(java);
        value[0] = 'g';
        System.out.println("new value:" + java);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

运行结果:

old value:java
new value:gava

String 对 “+” 的重载

    public static void main(String[] args) {
         String string = "hello";
         String string2 = string + "world";
    }

反编译查看

    public static void main(String args[]){
       String string = "hollis";
       String string2 = (new 		
				StringBuilder(String.valueOf(string))).append("chuang").toString();
    }

String.valueOf 和 Integer.toString 的区别

下面三种方式都能将一个 int 类型的变量变成 String 类型,他们的区别是什么呢?

    int i = 5;
    String i1 = "" + i;
    String i2 = String.valueOf(i);
    String i3 = Integer.toString(i);
  1. 第三行和第四行没有任何区别,因为 String.valueOf(i) 也是调用 Integer.toString(i) 来实现的。
  2. 第二行其实首先基于原字符串创建了一个 StringBuilder 对象,然后依次调用 append 方法、toString 方法。
java.lang包 132131222313213 立即下载

相关推荐

commons-lang3-3.1 StringUtils字符串jar包

commons-lang3-3.1 StringUtils字符串jar包 org.apache.commons.lang3.StringUtils的jar包

java lang包下载_java.lang 包(一)java.lang 包装类

实验介绍本实验将学习 java.lang 包内的包装类以及 String 类、Math 类、Class 类、Object 类的相关知识。知识点包装类String 类Math 类Class 类Object 类实验环境JDKWEB IDE实验步骤这一节我们将学习 java.lang 包中的常用类。Java API 简介Java 的核心 API 是非常庞大的,这给开发者来说带来了很大的方便。所谓的 AP...

weixin_39825045的博客 1173

java8源码-jdk8:jdk8源码阅读理解

java8 源码 jdk8 jdk8源码的阅读理解 导入idea步骤: 阅读顺序: 大致思路 基本类型的包装类(Character放在最后) String、StringBuffer、StringBuilder、StringJoiner、StringTokenizer(补充正则表达式的知识) CharacterIterator、StringCharacterIterator、CharsetProvider、CharsetEncoder、CharsetDecoder(较难) java.util.function下的函数表达式 java.nio下的各种Buffer实现 java.lang.ref和jdk.internal.ref下的各种引用:软引用/弱引用/虚引用 Unsafe的实现(JDK9之后有两个同名类,一个引用了另一个,建议放在一起阅读) java.util.stream下的流式编程的实现(很难) Thread和ThreadLocal Math、Random、BigInteger、BigDecimal java.lang.reflect下反射的实现(先掌握JDK 9之后引入的模块系统

【Java源码分析】Java8的String源码分析

学习Java8的String源码以及相关问题的总结 1. String的定义 2. 字段属性 3. 构造函数 4. 长度和是否为空函数 5. charAt、codePointAt类型函数 6. getChar、getBytes类型函数 7. equal类函数(是否相等) 8. regionMatchs()方法 9. compareTo类函数和CaseInsensitiveComparator静态内部类 10. startWith、endWith类函数 11. ha

长路漫漫的歇脚处 7444

java.lang包介绍

提供利用 Java 编程语言进行程序设计的基础类。最重要的类是 Object(它是类层次结构的根)和 Class(它的实例表示正在运行的应用程序中的类)。

莫兰指数stata命令_一文读懂16个sp系列stata空间计量命令及应用

spcompress:Compress Stata-format shapefile压缩数据成stata格式的shapefile2、Looking at data/观测数据2、Looking at data/观测数据grmap:Graph choropleth maps绘制等值区域图spdistance:Calculator for distance between places计算区域之间距离3...

weixin_39719989的博客 821

[新手向]Java常用包的免费+官方的下载/导入

今天写程序需要用到java-commons工具包。由于是第一次使用外部的包,所以在找相关资源和导入使用的时候遇到了一些问题。 现把问题和解决方法记录下来,供参考。 eclipse + Java 11 在哪里免费下载包? 因为我想实现的目的是:使用StringUtils这个工具类里的strip方法删除字符串两边的“[]”, 经过查询发现这...

记录 2888

从零开始开发AI Agent:基于大模型的智能体实战指南

### 摘要 本文介绍了AI Agent的开发全流程,从基础概念到实际部署,涵盖大模型集成、工具调用和记忆系统构建等核心技术。通过电商客服案例展示实战应用,并提供调试优化与部署方案,最后展望了AI Agent的未来发展方向,帮助开发者快速掌握智能体开发技能。

ghs_gss的博客 4860

java.lang.string 包_Java.lang Class

Java.lang Class1 Java.lang Class介绍Java Class类实例表示正在运行的Java应用程序中的类和接口。每个属于一个类的数组都是一个Class对象,并且由所有数组共享(具有相同的元素类型和维数)。2Java.lang Class声明public final class Classextends Objectimplements Serializable, Gen...

weixin_39805255的博客 552

从零开始:IBM V3700控制器更换与机器代码升级全流程指南

本文详细介绍了IBM V3700存储系统控制器更换与机器代码升级的全流程指南。从前期准备、硬件更换到版本同步,逐步解析如何应对503错误等常见问题,确保系统稳定运行。通过实战案例和优化建议,帮助工程师高效完成维护任务,提升存储系统可用性。

weixin_28365523的博客 312

自抗扰控制(ADRC)原理与MATLAB实现详解

自抗扰控制(ADRC)是一种先进的鲁棒控制策略,其核心在于通过扩张状态观测器(ESO)实时估计并补偿系统总扰动。相比传统PID控制,ADRC不依赖精确数学模型,特别适合处理非线性、时变系统。从控制原理看,ADRC将系统内部动态和外部扰动统一建模,通过带宽参数化方法实现快速响应与强抗扰能力。在工程实践中,ADRC已成功应用于无人机飞控、伺服系统等场景,MATLAB仿真显示其上升时间可比PID提升46%,抗扰动恢复时间缩短60%。典型实现包含ESO设计、扰动补偿和PD控制三部分,参数整定遵循ω₀=3~5倍系统带

weixin_33585822的博客 94

【JDK源码】java.lang包常用类详解

&nbsp;&nbsp;接下来的几天开始JDK源码的学习和总结,之前看《java编程思想》的时候看到java的基础知识有很多,其中支撑着这些基础的基础中的基础当属JDK。JDK的基础代码里面又分了很多基础的模块,今天从最基础的java.lang包开始学习。本次JDK源码学习系列以JDK1.7为准。 &nbsp;&nbsp;观察JDK的源码,可以看到,java.lang包下面最为基础和常用的几个部...

lmb55的专栏 6556

计算机组成原理(第三版)唐朔飞-第十章控制单元的设计-课后习题

计算机组成原理(第三版)唐朔飞-第十章控制单元的设计-课后习题

weixin_45735391的博客 3万+

Java.lang包源码学习

java源码学习Java.lang包接口摘要Iterable 接口Readable接口Runnable 接口Java.lang 包下类摘要Integer类 Java.lang包接口摘要 Iterable 接口 参考链接 iterable 的主要作用是实现for each方法,遍历集合的工作依然是iterator来做 default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action);

区小升的博客 1141

《网络安全自学教程》- MySQL脱库原理和步骤

MySQL脱库,脱库的原理,怎么脱库,脱库的步骤,一库三表六字段

wangyuxiang946的博客 1万+

032、准谐振反激变换器(QR-Flyback)原理

文章摘要: 本文以65W PD快充炸管案例切入,解析准谐振反激变换器(QR-Flyback)原理。通过主动利用漏感与MOS结电容的谐振,在Vds波谷开通实现近似ZVS,显著降低开关损耗与电压应力。对比传统反激,QR模式轻载效率提升8%,Vds尖峰减少23%。关键设计要点包括:变压器漏感需<3%、谷底检测电路需抗干扰、启动阶段需防过冲。推荐NCP1340等专用控制芯片,并强调VCC电容优选低ESR陶瓷材质。QR适用于30W-150W场景,其核心优势在于轻载效率优化,但需平衡漏感控制与可靠性。作者结合实测数据与

Jack010169的博客 112

JDK1.8源码阅读记录lang包String类

JDK1.8源码阅读记录 JAVA.LANG包 String类 public final class String implements java.io.Serializable, Comparable<String>, CharSequence 说明 final关键字修饰的类,说明该类不能被继承。 ...

weixin_43459704的博客 336

OTG原理讲解

摘要:USB OTG(On-The-Go)是USB 2.0的重要功能,允许设备在主机和从机间动态切换,实现设备间的直接通信。其核心机制是通过ID引脚电平判断角色:Micro-USB中接地为Host,悬空为Device;USB-C则通过CC引脚协商。电路设计需处理ID引脚连接和VBUS电源控制,MCU根据ID状态配置USB控制器模式。典型应用包括手机连接U盘、键盘等外设,极大扩展了移动设备的连接能力。

m0_49476241的博客 3217

简化 java.lang.String 源码

下面的注释按照重要性对源码进行编排 其中hashCode很重要,在HashMap中经常作为面试题。 因此整理后的源码,重点在前面部分 public final class String implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc { @java.io.Serial private static final long serialVersion

诗水人间 8万+

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

JAVA JDK源码在线阅读

Java的版本是1.8.0_111,我把JDK源码发布到了github上,大家看起来也比较方便,地址: https://github.com/daiqingliang/java_jdk1.8.0_111 在提供一个rt.jar包源码的地址(JDK源码中用了很多rt.jar中的类,例如sun.misc.Unsafe类,都包含在rt.jar中): https://github.com/daiqi...

qingliang4321的专栏 1万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

java源码探究——String

String类源码探究 目录 String类源码探究 1、写在前面的话 2、String类 3、String中的常见方法解析 3.1、构造方法 3.1.1、public String() 3.1.2、public String(String original) 3.3.3、public String(char value[])与public String(char value[...

wolf的博客 2932

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

JDK1.8源码阅读(1)--Object类和String类

JDK8源码阅读(1) java.lang 一.Object类 java所有类的祖先。 类图: 依次分析下这几个方法: 1.void registerNatives() 先定义了registerNatives()方法,然后当该类被加载的时候,调用该方法完成对该类中本地方法的注册。 2.Class<?> getClass() 返回当前对象所属的类对象。 3.int hashCode()...

jacobbbbbbbb的博客 455

java.lang.string源码_JDK1.8源码——java.lang.String 类

String 类也是java.lang 包下的一个类,算是日常编码中最常用的一个类了,那么本篇博客就来详细的介绍 String 类。1、String 类的定义public final class Stringimplements java.io.Serializable, Comparable, CharSequence {}和上一篇博客所讲的 Integer 类一样,这也是一个用 final 声...

weixin_42600407的博客 254

java-JDK8基本源码源码分析

JDK源码分析 基于JDK1.8------java lang Object Object的类中的方法(13个方法) registerNatives() getClass() hashCode() equals() clone() toString() notify() notifyAll() wait() 有三个方法重载 finalize() 源码: /** * Class {@code Object} is the root of the class hierarchy. * Every class

weixin_45376268的博客 1661

JDK-java.lang包详解

上一篇写了jdk源码阅读开端,这篇开始详细介绍一下核心包。 java.lang包是Java开发的基础包,lang包根目录下面放的是包装类,异常类,线程类等,lang包下面还有几个包: java.lang.annotation:放置元注解 java.lang.instrment:java.lang.instrument包的最大功能就是可以在已有的类上附加(修改)字节码来实现增强的逻辑 java.lang.invoke:提供了与Java虚拟机交互的低级原语 java.lang.man...

作者:大头兵(kit) 958

JDK(jdk1.8)源码剖析之 java.lang.String

String类位于JDK下rt.jar包中,完整包路径是java.lang.String。 jdk1.8.0_191下String的源码如下:jdk1.8_String源码,点击下载。 本文章只对String类中部分函数做一些描述、理解、及阅读。 下面以 /** * Splits this string around matches of the given * <a href="../util/regex/Pattern.html#sum">regula..

dnbug Blog 434
上一篇: Java8源码 java.lang 包 01、Object 类
下一篇: Java8源码 java.lang 包 03、AbstractStringBuilder 类
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值