java进阶-第四讲-深入理解String

java进阶-第四讲-深入理解String

1 认识new String(String original)

API中的String类的new String(String original)构造方法的实现:
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    
String类有两个成员属性:
private final char value[];
private int hash; // Default to 0
// 正常的有参构造应该是如下写法
public String(char[] value, int hash) {
    this.value = value;
    this.hash = hash;
}

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

1. value是什么类型的?char[] 
    char[]是什么数据类型类?引用数据类型,数组也是对象
    (除去byte short int long double float boolean char,其他的在java中学的任何类型都是引用数据类型。)
    this.value = original.value; 这个赋值语句怎么执行的?
        original.value是内存地址,是谁的地址?original对象中数组的地址。
        问题来了,真正在调用构造方法的时候,是这样的:
        String str = new String("hello");
		问大家:
            构造方法中的形式参数:String original
            调用时传入的实际参数:"hello"
            String original = "hello";
		请问:字面量"hello"是一个常量,在方法区内存中的字符串常量池中。
            字符串常量池中存放的字符串是以什么形式存放的?对象
            String是引用类型,它表示字符串这一类对象。
            "abc"是不是具体的某一个字符串?是的。
            "abc"是不是就是一个字符串对象?
            其实,在字符串常量池中存放的是String对象。
            如果,字符串在常量池中以值的形式存放,那么字符串一定是一个基础数据类型。
            因为只有基础数据类型才以值的形式直接存放。
  • 证明“abc”是对象:
System.out.println(ClassLayout.parseInstance("abc").toPrintable());

所得结果:
java.lang.String object internals:
 OFFSET  SIZE     TYPE DESCRIPTION                               VALUE
      0     4          (object header)                           01 00 00 00 
      4     4          (object header)                           d0 32 40 15 
      8     4   char[] String.value                              [a, b, c]
     12     4      int String.hash                               0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

"abc"这个字面量有value属性,有hash属性,是字符串对象。
  • 字面量“abc”什么时候创建的对象?
类加载的时候。以String类型的对象的结构在方法区内存中的常量池中进行存储。
常量池:除了有字符串常量之外,还有很多东西。比如类名、方法名、参数列表....
我们所看到的字符串,操作的字符串是"....",其实,在内存中存储的并不是这个样子,是char[]
char[] 是整型的形式,整型又被换算成了二进制."xxx"内存中没有这种格式的内容存在,这是一个对象,是复杂的结构,是复合的结构。它包括了对象头、包括成员属性、填充。
为什么要有字符串,因为人最容易理解,所以人看到的形式都是"xxx",但是机器里面存的不是这种形式。我告诉你,"xxx"这种形式是输出后的结果,输入的时候以"xxx"这种形式输入,在计算机中,把它定义为String,但是在存储的时候会将"xxx"这种格式的内容拆解为char[]数组进行存储。输出的时候,char[]中的内容一个连着一个打印到屏幕上。
  • 看看new String(String original)到底是怎么执行的。
String str = new String("he");

在执行的时候,首先,一定是在堆内存中创建了一个String对象。
    该对象有char[] value属性
    该对象有int hash属性
    
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

常量池中在类加载的时候已经有了"he"这个对象

在这里插入图片描述

  • 如果new String(“abc”);是上图所示的话,堆中实际上只有一个String类型的对象,它不是真正的“he”,真正的“he”是在常量池中的。因为字符串实现的核心是字符数组。那么也就意味着堆中没有“he”实际的值,如下图:

    在这里插入图片描述

  • 事实如此吗?并不是,我们来看,堆中String类型的对象布局,如下:

String str = new String("abc");
System.out.println(ClassLayout.parseInstance(str).toPrintable());

..........................................
java.lang.String object internals:
 OFFSET  SIZE     TYPE DESCRIPTION                               VALUE
      0     4          (object header)                           09 00 00 00 
      4     4          (object header)                           d0 32 40 15 
      8     4   char[] String.value                              [a, b, c]
     12     4      int String.hash                               0

    
char[] value是一个引用类型,从表面上看,
    它保存的是常量池中"abc"对象的char[] value数组的内存首地址。
    那么,我们在观察堆内存空间中的String类型对象的内部结构的时候
    char[] value应该保存的是一个引用,也就是 (object)
例如:User对象的内存结构,引用的表示形式如下:
User object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                    VALUE
      0     4              (object header)                   09 00 00 00 
      4     4              (object header)                   18 2a 04 15
      8     4                int User.id                     1
     12     4   java.lang.String User.name                   (object)
     16     4   java.lang.Object User.object                 (object)
     
 我们现在发现堆中的String类型的对象中的char[] value是:
    8     4   char[] String.value            [a, b, c] 
    而不是
    8     4   char[] String.value            (object)
 这说明什么问题?
     堆中到底有没有"he"真正的值?
     也就是说直接从堆中的String类型的对象能不能直接输出得到拿到"he"?
     或者是说,堆中char[] value这个数组存放的到底是引用还是真正的开辟了一个数组?
     并且在堆中char[] value数组中存放了[a,b,c]?
     
     我们看到的结果是,堆中真真正正的有char[] value = {'a','b','c'}
结论:
    堆中也创建了一个String类型的对象,也保存了"he"。也就是说"he"在堆中也存在。
     
    其实,数组是引用类型,但是它在作为参数传递的时候,实际上是真正将内容的复制了一份给到了对方。
  所以,new String("abc")实际上在堆中也有一份"abc",在常量池中也有一份"abc"
    当然重复了,因为重复,所以不建议你这么用。
  • 关于String类的hashCode()
    • 1.没有调用hashCode()方法之前,String对象的hash属性值是默认为0的
    • 2.只有调用了hashCode()方法,才会给对象的hash属性赋值。
    • 3.所有的对象都如此,对象被创建的时候hashcode值默认为0,只有当hashCode()方法被调用的时候,对象才会有hashcode值
    • 为什么String对象的hash值是不变的?因为String对象是常量。它不是靠地址进行计算的。是靠字面量进行计算。

2 关于字符串拼接

public class StringTest03 {
    public static void main(String[] args) {
        String str = "ab" + "cd";
        String str1 = str + "ef";
    }
}

关于"ab""cd"
在底层,上面的代码,在方法区内存空间中的字符串常量池中,只创建了一个字符串对象
    "abcd"
     #21 = Utf8   abcd
为什么呢?编译器是怎么做的?
    编译的时候,编译器会对"ab" + "cd"这样的操作进行优化。先拼接成"abcd"然后再放入常量表中。
    
    
关于str + "ef":    
    String str1 = str + "ef";// 这句话中含有3个对象。
        // 编译阶段:常量池中并没有"abcdef"这个对象
        // 这个对象实际是在运行阶段存入常量池中的
public class StringTest03 {
    public static void main(String[] args) {
        String str = "ab" + "cd";// abcd 编译器在编译阶段就优化了

        String str1 = str + "ef";// 这句话中含有3个对象。
        // 编译阶段:常量池中并没有"abcdef"这个对象
        // 这个对象实际是在运行阶段存入常量池中的
        
        
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入你想要的字符串: ");
        String next = sc.next();
        test(next);
        System.out.println(next.hashCode());
    }

    public static void test(String str) {
        System.out.println("输出你想要打印的内容: " + str);
    }
}

3 String类的使用

注意:
	在java中,不到万不得已不要去new字符串。使用String str = "xxx"的方式是最好的。
  • String的源码分析:
1. 构造方法:
public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
// 使用char[] value字符数组构造字符串
// 实现:
// 	数组的拷贝。
//  将传入的实参char[] value的内容拷贝到String对象的成员属性char[] value中

// 这里的Arrays.copyOf(value, value.length)调用了System类中的arraycopy()
    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
// 这么调用的好处是,不需要写更多的代码,
// 但是又能简化原有的System.arraycopy方法的参数列表的个数。
// 补充: 在设计程序的时候,尤其是方法的参数个数,最多不要超过五个!!!!
// 		方法的参数一旦超过五个之后,可读性很差。使用起来也不方便。

public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
这个构造方法是将字节数组构造成字符串。
该方法在应用中,一般都是从IO流中读到的bytes,转换成字符串。
    网络中无法传输字符串,都是打成字节流进行传送。
    1Byte---8bit
// 字节数组转换String的应用举例。
// 要注意的细节是,转换时,双发一定要知道字符编码的格式,格式要统一。
public class StringTest03 {
    public static void main(String[] args) throws UnsupportedEncodingException {

        String str1 = "老师你好";
        byte[] strBytes = str1.getBytes("GBK");
        for (int i = 0; i < strBytes.length; i++) {
            System.out.print("0x" +Integer.toHexString(strBytes[i]) + " ");
        }
        System.out.println();
        String string = new String(strBytes, "GBK");
        System.out.println(string);

        byte[] bytes = new byte[3];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (96 + i);
        }
        String str = new String(bytes, "utf8");
        System.out.println(str);
    }
}
  • 应用演示程序:对文本的加解密
package com.xx.encryptAndDecrypt.enCrypt;

import java.io.UnsupportedEncodingException;

/**
 * @program: Study
 * @description: 对字符串加密
 * @author: xx
 * @create: 2021-02-04 11:03
 */
public class Encrypt {
    public static byte[] getBytesFromText(String text, String charsetName) throws UnsupportedEncodingException {
        return text.getBytes(charsetName);
    }

    public static byte[] encrypt(byte[] bytes) {
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] += 3;
        }
        return bytes;
    }

    public static void printEncryptText(byte[] bytes) {
        for (int i = 0; i < bytes.length; i++) {
            System.out.print("0x" + Integer.toHexString(bytes[i]) + " ");
        }
        System.out.println();
    }
}

package com.xx.encryptAndDecrypt.decrypt;

import java.io.UnsupportedEncodingException;

/**
 * @program: Study
 * @description: 解密
 * @author: xx
 * @create: 2021-02-04 11:09
 */
public class Decypt {

    public static String decrypt(byte[] bytes, String charsetName) throws UnsupportedEncodingException {
        return new String(bytes, charsetName);
    }

    public static byte[] getRealBytes(byte[] bytes) {
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] -= 3;
        }
        return bytes;
    }
}

  • 测试程序
package com.xx.encryptAndDecrypt;

import com.xx.encryptAndDecrypt.decrypt.Decypt;
import com.xx.encryptAndDecrypt.enCrypt.Encrypt;

import java.io.UnsupportedEncodingException;
import java.util.Scanner;

/**
 * @program: Study
 * @description:
 * @author: xx
 * @create: 2021-02-04 11:12
 */
public class Test {
    public static void main(String[] args) throws UnsupportedEncodingException {
        Scanner scanner = new Scanner(System.in);
        String text = scanner.next();

        byte[] textToBytes = Encrypt.getBytesFromText(text, "utf8");
        byte[] encrypt = Encrypt.encrypt(textToBytes);
        Encrypt.printEncryptText(encrypt);
        System.out.println("--------加密结束-----");

        byte[] realBytes = Decypt.getRealBytes(encrypt);
        String decryptText = Decypt.decrypt(realBytes, "utf8");
        System.out.println(decryptText);
    }
}

  • 注意:在java中字符数组的末尾没有‘\0’
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值