百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

从JDK源码看String(上)

toyiye 2024-06-21 12:27 10 浏览 0 评论

概况

Java 语言使用 String 类用来代表字符串,实际上 String 对象的值是一个常量,一旦创建后不能被改变。正式因为其不可变,所以它是线程安全地,可以多个线程共享。

相信对于 String 的使用大家都再熟悉不过的了,这里就了解下 JDK 中怎么实现 String 类的。

继承结构

--java.lang.Object

--java.lang.String

类定义

public final class String implements java.io.Serializable, Comparable<String>, CharSequence

String 类被声明为 final,说明它不能再被继承。同时它实现了三个接口,分别为 Serializable、Comparable 和 CharSequence。其中 Serializable 接口表明其可以序列化;

InputStream 被定为 public 且 abstract 的类,实现了Closeable接口。

Closeable 接口表示 InputStream 可以被close,接口定义如下:

public interface Closeable extends AutoCloseable {

public void close() throws IOException;

}

主要属性

private final byte[] value;

private final byte coder;

private int hash;

static final boolean COMPACT_STRINGS;

static {

COMPACT_STRINGS = true;

}

static final byte LATIN1 = 0;

static final byte UTF16 = 1;

public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();

  • value 用于存储字符串对象的值。
  • coder 表示字符串对象所用的编码器,为LATIN1或UTF16。
  • hash 为字符串对象的哈希值,默认值为0。
  • COMPACT_STRINGS 表示是否使用紧凑的字符数组布局,默认使用。
  • CASE_INSENSITIVE_ORDER 表示用于排序的比较器

内部类

该内部类主要是提供排序的比较器,实现了Comparator接口和compare方法,另外一个readResolve方法用于替换反序列化时的对象。compare核心方法的逻辑是,根据两者编码是否相同做处理,如果相同则分 Latin1 或 UTF16 两种情况比较,类似地,如果两者编码不同,则需要用 Latin1 编码与 UTF16 编码比较,而 UTF16 则要与 Latin1 比较。

private static class CaseInsensitiveComparator

implements Comparator<String>, java.io.Serializable {

private static final long serialVersionUID = 8575799808933029326L;

public int compare(String s1, String s2) {

byte v1[] = s1.value;

byte v2[] = s2.value;

if (s1.coder() == s2.coder()) {

return s1.isLatin1() ? StringLatin1.compareToCI(v1, v2)

: StringUTF16.compareToCI(v1, v2);

}

return s1.isLatin1() ? StringLatin1.compareToCI_UTF16(v1, v2)

: StringUTF16.compareToCI_Latin1(v1, v2);

}

private Object readResolve() { return CASE_INSENSITIVE_ORDER; }

}

构造方法

有很多种构造方法,看主要的几个。没有参数的构造方法直接将空字符串的 value 和 coder 进行赋值。

public String() {

this.value = "".value;

this.coder = "".coder;

}

类似的,传入 String 对象的构造方法则将该对象对应的 value 、coder 和 hash 进行赋值。

public String(String original) {

this.value = original.value;

this.coder = original.coder;

this.hash = original.hash;

}

构造方法传入 char 数组时,主要逻辑就是如果 COMPACT_STRINGS 为 true,即使用紧凑布局的话,则尝试将其转换成为 LATIN1 编码(即ISO-8859-1编码),这里说尝试是因为 char 数组中可能包含了非 LATIN1 编码,此时是压缩失败的,只有数组中全部都为 LATIN1 编码时才能压缩成功。类似的还有传入 int 数组的,int 类型占用4个字节,只有全部符合转换才能转成 LATIN1 编码。

public String(char value[]) {

this(value, 0, value.length, null);

}

String(char[] value, int off, int len, Void sig) {

if (len == 0) {

this.value = "".value;

this.coder = "".coder;

return;

}

if (COMPACT_STRINGS) {

byte[] val = StringUTF16.compress(value, off, len);

if (val != null) {

this.value = val;

this.coder = LATIN1;

return;

}

}

this.coder = UTF16;

this.value = StringUTF16.toBytes(value, off, len);

}

构造方法传入 byte 数组时,同时会传入 charsetName,即编码。核心操作为StringCoding.decode,它会先根据编码对 byte 数组进行解码,解码过程会判断是否全部都在 LATIN1 编码内,如果是则使用 LATIN1 编码,否则使用 UTF16 编码,并且将解码后对应的 byte 数组赋值给 String 对象的 value。

public String(byte bytes[], int offset, int length, String charsetName)

throws UnsupportedEncodingException {

if (charsetName == null)

throw new NullPointerException("charsetName");

checkBoundsOffCount(offset, length, bytes.length);

StringCoding.Result ret =

StringCoding.decode(charsetName, bytes, offset, length);

this.value = ret.value;

this.coder = ret.coder;

}

主要方法

length方法

字符串的长度应该是字符的长度,而不是字节数组的长度,所以这里做了右移操作,LATIN1 编码时coder()为0,字符串长度等于字节数组长度。UTF16 编码时coder()为1,字符串等于字节数组长度一半。

public int length() {

return value.length >> coder();

}

isEmpty方法

通过判断 byte 数组长度是否为0来判断字符串对象是否为空。

public boolean isEmpty() {

return value.length == 0;

}

charAt方法

取字符需要根据编码来操作,如果是 LATIN1 编码,则直接取 byte 数组中对应索引的元素,并转成 char 类型即可。如果是 UTF16 编码,因为它每个 UTF16 编码占用两个字节,所以需要将索引乘以2后作为最终索引取得两个字节并转换成 char 类型,具体实现逻辑如getChar方法所示。

public char charAt(int index) {

if (isLatin1()) {

return StringLatin1.charAt(value, index);

} else {

return StringUTF16.charAt(value, index);

}

}

static char getChar(byte[] val, int index) {

index <<= 1;

return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |

((val[index] & 0xff) << LO_BYTE_SHIFT));

}

codePointAt方法

获取字符串对应索引的 Unicode 代码点,根据编码做不同处理。如果是 LATIN1 编码,直接将 byte 数组对应索引的元素与0xff做&操作并转成 int 类型。相应的,UTF16 编码也需要对应做转换,它包含了两个字节。

public int codePointAt(int index) {

if (isLatin1()) {

checkIndex(index, value.length);

return value[index] & 0xff;

}

int length = value.length >> 1;

checkIndex(index, length);

return StringUTF16.codePointAt(value, index, length);

}

codePointBefore方法

用于返回指定索引值前一个字符的代码点,实现与codePointAt方法类似,只是索引值要减1。

public int codePointBefore(int index) {

int i = index - 1;

if (i < 0 || i >= length()) {

throw new StringIndexOutOfBoundsException(index);

}

if (isLatin1()) {

return (value[i] & 0xff);

}

return StringUTF16.codePointBefore(value, index);

}

codePointCount方法

用于得到指定索引范围内代码点的个数,如果是 Latin1 编码则直接索引值相减,因为每个字节肯定都属于一个代码点。如果是 UTF16 编码则要检查是否存在 High-surrogate 代码和 Low-surrogate 代码,如果存在则说明需要4个字节来表示一个字符,此时要把 count 减1。

public int codePointCount(int beginIndex, int endIndex) {

if (beginIndex < 0 || beginIndex > endIndex ||

endIndex > length()) {

throw new IndexOutOfBoundsException();

}

if (isLatin1()) {

return endIndex - beginIndex;

}

return StringUTF16.codePointCount(value, beginIndex, endIndex);

}

private static int codePointCount(byte[] value, int beginIndex, int endIndex, boolean checked) {

assert beginIndex <= endIndex;

int count = endIndex - beginIndex;

int i = beginIndex;

if (checked && i < endIndex) {

checkBoundsBeginEnd(i, endIndex, value);

}

for (; i < endIndex - 1; ) {

if (Character.isHighSurrogate(getChar(value, i++)) &&

Character.isLowSurrogate(getChar(value, i))) {

count--;

i++;

}

}

return count;

}

public static int codePointCount(byte[] value, int beginIndex, int endIndex) {

return codePointCount(value, beginIndex, endIndex, false /* unchecked */);

}

offsetByCodePoints方法

该方法用于返回 String 中从给定的 index 处偏移 codePointOffset 个 Unicode 代码点的索引,要注意 Unicode 代码可能两个字节也可能四个字节。逻辑为:

  • index 不能小于0且不能超过。
  • 当 codePointOffset 大于0时,通过 for 循环递增索引值,判断如果存在 High-surrogate 代码和 Low-surrogate 代码则索引值还需额外加1。
  • 当 codePointOffset 小于0时,通过 for 循环递减索引值,判断如果存在 High-surrogate 代码和 Low-surrogate 代码则索引值还需额外减1。

public int offsetByCodePoints(int index, int codePointOffset) {

if (index < 0 || index > length()) {

throw new IndexOutOfBoundsException();

}

return Character.offsetByCodePoints(this, index, codePointOffset);

}

public static int offsetByCodePoints(CharSequence seq, int index,

int codePointOffset) {

int length = seq.length();

if (index < 0 || index > length) {

throw new IndexOutOfBoundsException();

}

int x = index;

if (codePointOffset >= 0) {

int i;

for (i = 0; x < length && i < codePointOffset; i++) {

if (isHighSurrogate(seq.charAt(x++)) && x < length &&

isLowSurrogate(seq.charAt(x))) {

x++;

}

}

if (i < codePointOffset) {

throw new IndexOutOfBoundsException();

}

} else {

int i;

for (i = codePointOffset; x > 0 && i < 0; i++) {

if (isLowSurrogate(seq.charAt(--x)) && x > 0 &&

isHighSurrogate(seq.charAt(x-1))) {

x--;

}

}

if (i < 0) {

throw new IndexOutOfBoundsException();

}

}

return x;

}

getChars方法

用于获取字符串对象指定范围内的字符到目标 char 数组中,主要是根据两种编码做不同处理,如果是 LATIN1 编码则直接将 byte 数组对应索引的元素与0xff做&操作并转成 char 类型。而如果是 UTF16 编码则需要两个字节一起转为 char 类型。

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {

checkBoundsBeginEnd(srcBegin, srcEnd, length());

checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);

if (isLatin1()) {

StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin);

} else {

StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin);

}

}

getBytes方法

获取字符串指定编码的字节数组,比如 charsetName 为 utf8,则将字符串转为 utf8 编码后对应的字节数组。如果不传参数则使用 JVM 默认编码,即Charset.defaultCharset()。

public byte[] getBytes(String charsetName)

throws UnsupportedEncodingException {

if (charsetName == null) throw new NullPointerException();

return StringCoding.encode(charsetName, coder(), value);

}

public byte[] getBytes() {

return StringCoding.encode(coder(), value);

}

equals方法

用于比较两字符串对象是否相等,如果引用相同则返回 true。否则判断比较对象是否为 String 类的实例,是的话转成 String 类型,接着比较编码是否相同,分别以 LATIN1 编码和 UTF16 编码进行比较。

public boolean equals(Object anObject) {

if (this == anObject) {

return true;

}

if (anObject instanceof String) {

String aString = (String)anObject;

if (coder() == aString.coder()) {

return isLatin1() ? StringLatin1.equals(value, aString.value)

: StringUTF16.equals(value, aString.value);

}

}

return false;

}

contentEquals方法

该方法用于比较字符串之间内容是否相等,逻辑为:

  • 是否由 AbstractStringBuilder 实例化出来,是的话表示它为 StringBuilder 或 StringBuffer 对象。
  • 如果为 StringBuffer 对象,说明它是线程安全的,需要做同步处理,调用nonSyncContentEquals方法。
  • 如果为 StringBuilder 对象,它是非线程安全的,直接调用nonSyncContentEquals方法。
  • 如果为 String 对象,则调用equals方法比较。
  • 接下去的逻辑属于 CharSequence 对象时的逻辑。如果长度不相等直接返回 false。
  • 如果为 Latin1 编码,则只比较一个字节。
  • 如果为 UTF16 编码,则要两个字节地比较。

public boolean contentEquals(CharSequence cs) {

if (cs instanceof AbstractStringBuilder) {

if (cs instanceof StringBuffer) {

synchronized(cs) {

return nonSyncContentEquals((AbstractStringBuilder)cs);

}

} else {

return nonSyncContentEquals((AbstractStringBuilder)cs);

}

}

if (cs instanceof String) {

return equals(cs);

}

int n = cs.length();

if (n != length()) {

return false;

}

byte[] val = this.value;

if (isLatin1()) {

for (int i = 0; i < n; i++) {

if ((val[i] & 0xff) != cs.charAt(i)) {

return false;

}

}

} else {

if (!StringUTF16.contentEquals(val, cs, n)) {

return false;

}

}

return true;

}

nonSyncContentEquals方法逻辑为:

  • 判断两个长度不相等则返回 false。
  • 如果两者的编码相同,则一个个字节比较。
  • 两者编码不同时,如果本身编码为 Latin1 编码,那就直接返回 false,而如果本身为 UTF16 编码,则两个字节地比较。

private boolean nonSyncContentEquals(AbstractStringBuilder sb) {

int len = length();

if (len != sb.length()) {

return false;

}

byte v1[] = value;

byte v2[] = sb.getValue();

if (coder() == sb.getCoder()) {

int n = v1.length;

for (int i = 0; i < n; i++) {

if (v1[i] != v2[i]) {

return false;

}

}

} else {

if (!isLatin1()) {

return false;

}

return StringUTF16.contentEquals(v1, v2, len);

}

return true;

}

equalsIgnoreCase方法

该方法用于对比字符串是否相等,而且是忽略大小写。如果是自己与自己对比则不为空则为true,否则需要两者长度相等且regionMatches方法返回true才为true。

public boolean equalsIgnoreCase(String anotherString) {

return (this == anotherString) ? true

: (anotherString != null)

&& (anotherString.length() == length())

&& regionMatches(true, 0, anotherString, 0, length());

}

regionMatches方法逻辑为:

  • ignoreCase 如果为 false,即不忽略大小写,则需要调另外一个regionMatches方法,这里为 true,忽略此方法。
  • 校验 offset 不能小于0且不能大于待比较长度。
  • coder() == other.coder()为 true,即两者编码一样时,如果为 Latin1 编码,则以 Latin1 方式比较,否则以 UTF16 方式比较。
  • 如果两者编码不同,则以 Latin1 编码与 UTF16 编码比较,主要就是将 Latin1 转为 UTF16。而如果是 UTF16 编码的话则将另外一个的 Latin1 转为 UTF16 后比较。

public boolean regionMatches(boolean ignoreCase, int toffset,

String other, int ooffset, int len) {

if (!ignoreCase) {

return regionMatches(toffset, other, ooffset, len);

}

if ((ooffset < 0) || (toffset < 0)

|| (toffset > (long)length() - len)

|| (ooffset > (long)other.length() - len)) {

return false;

}

byte tv[] = value;

byte ov[] = other.value;

if (coder() == other.coder()) {

return isLatin1()

? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len)

: StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len);

}

return isLatin1()

? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)

: StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);

}

compareTo方法

该方法用于比较两个字符串,主要的逻辑为:

  • coder() == anotherString.coder(),即两者编码相同时,如果为 Latin1 编码则以 Latin1 的方式进行比较。否则以 UTF16 方式进行比较,具体如何比较下面以 Latin1 编码为例子。
  • 两者编码不同时,则将 Latin1 编码转成 UTF16 后进行比较。

public int compareTo(String anotherString) {

byte v1[] = value;

byte v2[] = anotherString.value;

if (coder() == anotherString.coder()) {

return isLatin1() ? StringLatin1.compareTo(v1, v2)

: StringUTF16.compareTo(v1, v2);

}

return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)

: StringUTF16.compareToLatin1(v1, v2);

}

Latin1 编码的比较逻辑为:

  • 首先分别获取两者的长度。
  • 选取两者最小的长度。
  • 开始遍历寻找两者不相同的字符,分别获取对应的 char 值并相减,大于0则说明第一个字符串大。
  • 如果遍历完都相等,那么就看谁的长度长,第一个字符串长度较长就返回大于0。

public static int compareTo(byte[] value, byte[] other) {

int len1 = value.length;

int len2 = other.length;

int lim = Math.min(len1, len2);

for (int k = 0; k < lim; k++) {

if (value[k] != other[k]) {

return getChar(value, k) - getChar(other, k);

}

}

return len1 - len2;

}

compareToIgnoreCase方法

该方法类似 compareTo 方法,只是忽略大小写。实现通过CaseInsensitiveComparator内部类来实现。

public int compareToIgnoreCase(String str) {

return CASE_INSENSITIVE_ORDER.compare(this, str);

}

regionMatches方法

该方法用于检测指定区域字符串是否相等,其逻辑为:

  • ignoreCase 如果为 false,即不忽略大小写,则需要调另外一个regionMatches方法。
  • 校验 offset 不能小于0且不能大于待比较长度。
  • coder() == other.coder()为 true,即两者编码一样时,如果为 Latin1 编码,则以 Latin1 方式比较,否则以 UTF16 方式比较。
  • 如果两者编码不同,则以 Latin1 编码与 UTF16 编码比较,主要就是将 Latin1 转为 UTF16。而如果是 UTF16 编码的话则将另外一个的 Latin1 转为 UTF16 后比较。

public boolean regionMatches(boolean ignoreCase, int toffset,

String other, int ooffset, int len) {

if (!ignoreCase) {

return regionMatches(toffset, other, ooffset, len);

}

if ((ooffset < 0) || (toffset < 0)

|| (toffset > (long)length() - len)

|| (ooffset > (long)other.length() - len)) {

return false;

}

byte tv[] = value;

byte ov[] = other.value;

if (coder() == other.coder()) {

return isLatin1()

? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len)

: StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len);

}

return isLatin1()

? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)

: StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);

}

大小写敏感的比较逻辑:

  • 检验两者的偏移的合法性,不能小于0,也不能超出特定长度。
  • 如果coder() == other.coder(),即两者编码相同时,如果为 Latin1 编码则直接比较每个字节,而如果为 UTF16 编码则需要将位移和长度都扩大一倍,因为 UTF16 占用的空间是 Latin1 的两倍,然后再比较每个字节是否相等。
  • 如果两者编码不相同时,不管两者谁是 Latin1 还是 UTF16 编码,都将它们转换成 char 类型再比较。

public boolean regionMatches(int toffset, String other, int ooffset, int len) {

byte tv[] = value;

byte ov[] = other.value;

if ((ooffset < 0) || (toffset < 0) ||

(toffset > (long)length() - len) ||

(ooffset > (long)other.length() - len)) {

return false;

}

if (coder() == other.coder()) {

if (!isLatin1() && (len > 0)) {

toffset = toffset << 1;

ooffset = ooffset << 1;

len = len << 1;

}

while (len-- > 0) {

if (tv[toffset++] != ov[ooffset++]) {

return false;

}

}

} else {

if (coder() == LATIN1) {

while (len-- > 0) {

if (StringLatin1.getChar(tv, toffset++) !=

StringUTF16.getChar(ov, ooffset++)) {

return false;

}

}

} else {

while (len-- > 0) {

if (StringUTF16.getChar(tv, toffset++) !=

StringLatin1.getChar(ov, ooffset++)) {

return false;

}

}

}

}

return true;

}

startsWith方法

该方法用于检测字符串是否以某个前缀开始,并且可以指定偏移。逻辑为:

  • 检查偏移和长度的合法性。
  • 如果coder() == prefix.coder(),即两者编码相同时,如果为 Latin1 编码则直接比较每个字节是否相等,如果为 UTF16 编码则要将位移扩大一倍,再比较每个字节。
  • 如果两者编码不相同,如果字符串为 Latin1 编码而前缀字符串为 UTF16 编码则无法比较直接返回 false。如果字符串为 UTF16 而前缀字符串为 Latin1,则转为 UTF16 进行比较。

public boolean startsWith(String prefix) {

return startsWith(prefix, 0);

}

public boolean startsWith(String prefix, int toffset) {

if (toffset < 0 || toffset > length() - prefix.length()) {

return false;

}

byte ta[] = value;

byte pa[] = prefix.value;

int po = 0;

int pc = pa.length;

if (coder() == prefix.coder()) {

int to = isLatin1() ? toffset : toffset << 1;

while (po < pc) {

if (ta[to++] != pa[po++]) {

return false;

}

}

} else {

if (isLatin1()) {

return false;

}

while (po < pc) {

if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) {

return false;

}

}

}

return true;

}

endsWith方法

该方法用于检查是否以某个字符串结尾,间接调用startsWith方法即可实现。

public boolean endsWith(String suffix) {

return startsWith(suffix, length() - suffix.length());

}

hashCode方法

该方法返回字符串对象的哈希值,如果已经有缓存了则直接返回,否则根据不同编码分别计算哈希值。

public int hashCode() {

int h = hash;

if (h == 0 && value.length > 0) {

hash = h = isLatin1() ? StringLatin1.hashCode(value)

: StringUTF16.hashCode(value);

}

return h;

}

下面分别是 Latin1 编码和 UTF16 编码的哈希值计算逻辑,遍历地执行h = 31 * h + (v & 0xff)和 h = 31 * h + getChar(value, i)运算。

public static int hashCode(byte[] value) {

int h = 0;

for (byte v : value) {

h = 31 * h + (v & 0xff);

}

return h;

}

public static int hashCode(byte[] value) {

int h = 0;

int length = value.length >> 1;

for (int i = 0; i < length; i++) {

h = 31 * h + getChar(value, i);

}

return h;

}

indexOf方法

该方法用于查找字符串中第一个出现某字符或字符串的位置,有多种方法参数。可传入 int 类型,也可传入 String 类型,另外还能传入开始位置。根据编码的不同分别调用 StringLatin1 和 StringUTF16 的indexOf方法。

public int indexOf(int ch) {

return indexOf(ch, 0);

}

public int indexOf(int ch, int fromIndex) {

return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex)

: StringUTF16.indexOf(value, ch, fromIndex);

}

public int indexOf(String str) {

if (coder() == str.coder()) {

return isLatin1() ? StringLatin1.indexOf(value, str.value)

: StringUTF16.indexOf(value, str.value);

}

if (coder() == LATIN1) {

return -1;

}

return StringUTF16.indexOfLatin1(value, str.value);

}

public int indexOf(String str, int fromIndex) {

return indexOf(value, coder(), length(), str, fromIndex);

}

Latin1 编码查找逻辑,

  • 判断 int 值是否能转成 byte,方法是看右移8位是否为0,为0即说明除了低8位其他都为0。
  • 判断索引值的合法性并修正。
  • int 值转成 byte 类型。
  • 遍历检查数组中哪个值相等并返回对应索引值。
  • 查找不到就返回-1。

public static int indexOf(byte[] value, int ch, int fromIndex) {

if (!canEncode(ch)) {

return -1;

}

int max = value.length;

if (fromIndex < 0) {

fromIndex = 0;

} else if (fromIndex >= max) {

return -1;

}

byte c = (byte)ch;

for (int i = fromIndex; i < max; i++) {

if (value[i] == c) {

return i;

}

}

return -1;

}

public static boolean canEncode(int cp) {

return cp >>> 8 == 0;

}

类似地,对于 UTF16 编码也做类似处理,但因为 unicode 包含了基本多语言平面(Basic Multilingual Plane,BMP)外,还存在补充平面。而传入的值为 int 类型(4字节),所以如果超出 BMP 平面,此时需要4个字节,分别用来保存 High-surrogate 和 Low-surrogate,此时就需要对比4个字节。

另外,如果查找子字符串则是从子字符串第一个字符开始匹配直到子字符串完全被匹配成功。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码