【JavaSE】Java基础语法(三十七):Java 中的 String 类(源码级别)

news2024/11/25 8:15:35

文章目录

  • 1. 构造方法
    • 1.1 String()
    • 1.2 String(String original)
    • 1.3 String(char[] chars)
    • 1.4 String(char数组,起始下标,长度)
    • 1.5 String(byte数组)
    • 1.6 String(byte数组,起始下标,长度)
    • 1.7 String(StringBuffer buffer)
    • 1.8 String(StringBuilder builder)
  • 2. 普通方法
    • 2.1 char charAt(int index)
    • 2.2 int compareTo(String anotherString)
    • 2.3 int indexOf(String str, int fromIndex)
    • 2.4 int indexOf(String str)
    • 2.5 boolean contains(CharSequence s)
    • 2.6 boolean startsWith(String prefix, int toffset)
    • 2.7 boolean startsWith(String prefix)
    • 2.8 boolean endsWith(String suffix)
    • 2.9 boolean equals(Object anObject)
    • 2.10 boolean equalsIgnoreCase(String anotherString)
    • 2.11 char[] toCharArray()
    • 2.12 String substring(int beginIndex)
    • 2.13 String substring(int beginIndex, int endIndex)
    • 2.14 String replace(CharSequence target, CharSequence replacement)
    • 2.15 String[] split(String regex)
  • 3. 常见面试题
  • 4. 深入理解
    • 4.1“+”连接符
      • 4.1.1“+”连接符的实现原理
      • 4.1.2“+”连接符的效率
    • 4.2 字符串常量池
      • 4.2.1 内存区域
      • 4.2.2 存放的内容
      • 4.2.3 intern 方法
        • intern的用法
  • 5. String、StringBuilder和StringBuffer
    • 5.1 继承结构
    • 5.2 主要区别
  • 6. 总结
  • 7. 注意:
  • 8. 参考文献


在这里插入图片描述

String 表示 字符串类型,属于 引用数据类型 。Java 中 String 是 不可变 的。
在 Java 当中 双引号 括起来的字符串,是直接存储在“方法区”的“字符串常量池”当中的。


1. 构造方法

1.1 String()

源码:

/** 
    初始化新创建的字符串对象,使其表示空字符序列。
 请注意,由于字符串是不可变的,因此不需要使用此构造函数。
*/
public String() {
    this.value = "".value;
}

1.2 String(String original)

源码:

/**
    初始化新创建的字符串对象,使其表示与参数相同的字符序列;
    换句话说,新创建的字符串是参数字符串的副本。
    除非需要original的显式副本,否则不需要使用此构造函数,因为字符串是不可变的。
*/
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

1.3 String(char[] chars)

源码:

/**
分配一个新字符串,使其表示字符数组参数中当前包含的字符序列。
复制字符数组的内容;对字符数组的后续修改不会影响新创建的字符串。
参数:
value–字符串的初始值
*/
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}

1.4 String(char数组,起始下标,长度)

源码:

/**
    分配一个新字符串,该字符串包含字符数组参数子数组中的字符。
    offset参数是子数组第一个字符的索引,count参数指定子数组的长度。
    复制子数组的内容;对字符数组的后续修改不会影响新创建的字符串。
    参数:
    value–作为字符源的数组
    偏移量–初始偏移量
    计数–长度
    抛出:
    IndexOutOfBoundsException–如果偏移量和计数参数索引字符超出值数组的边界
*/
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);
}

1.5 String(byte数组)

源码:

/**
    通过使用平台的默认字符集对指定的字节数组进行解码来构造新字符串。
    新字符串的长度是字符集的函数,因此可能不等于字节数组的长度。
    当给定字节在默认字符集中无效时,此构造函数的行为未指定。
    当需要对解码过程进行更多控制时,应使用CharsetDecoder类。
参数:
字节–要解码为字符的字节
Since: JDK1.1
*/ 
public String(byte bytes[]) {
     this(bytes, 0, bytes.length);
 }

1.6 String(byte数组,起始下标,长度)

源码:

/**
    通过使用平台的默认字符集对指定的字节子数组进行解码,构造一个新字符串。
    新字符串的长度是字符集的函数,因此可能不等于子数组的长度。
    当给定字节在默认字符集中无效时,此构造函数的行为未指定。
    当需要对解码过程进行更多控制时,应使用CharsetDecoder类。
参数:
字节–要解码为字符的字节
偏移量–要解码的第一个字节的索引
长度–要解码的字节数
抛出:
IndexOutOfBoundsException–如果偏移量和长度参数索引字符超出字节数组的边界
Since: JDK1.1
*/ 
public String(byte bytes[], int offset, int length) {
    //	checkBounds(bytes, offset, length);
    if (length < 0)
        throw new StringIndexOutOfBoundsException(length);
    if (offset < 0)
        throw new StringIndexOutOfBoundsException(offset);
    if (offset > bytes.length - length)
        throw new StringIndexOutOfBoundsException(offset + length);
    
    this.value = StringCoding.decode(bytes, offset, length);
}

1.7 String(StringBuffer buffer)

源码:

/**
    分配一个新字符串,该字符串包含字符串缓冲区参数中当前包含的字符序列。
    复制字符串缓冲区的内容;
    字符串缓冲区的后续修改不会影响新创建的字符串。
*/
synchronized(buffer) {
    this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}

1.8 String(StringBuilder builder)

源码:

/**
    分配一个新字符串,该字符串包含字符串生成器参数中当前包含的字符序列。
    复制字符串生成器的内容;
    字符串生成器的后续修改不会影响新创建的字符串。
    提供此构造函数是为了方便迁移到StringBuilder。
    通过toString方法从字符串生成器获取字符串可能运行得更快,通常是首选方法。
    Since:JDK1.5
*/  
public String(StringBuilder builder) {
    this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

2. 普通方法

https://blog.csdn.net/qq_44715943/article/details/116308837

2.1 char charAt(int index)

源码:

/**
    返回指定索引处的字符值。索引的范围从0到length()-1。
    序列的第一个字符值在索引0处,下一个字符值在索引1处,依此类推,就像数组索引一样。
    如果索引指定的char值是代理项,则返回代理项值。
参数:index–字符值的索引。
返回:此字符串指定索引处的char值。第一个char值位于索引0处。
抛出:IndexOutOfBoundsException–如果索引参数为负或不小于此字符串的长度。
*/
public char charAt(int index) {
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index];
}

2.2 int compareTo(String anotherString)

源码:

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;
}

2.3 int indexOf(String str, int fromIndex)

源码:

/**
    返回指定子字符串第一次出现的字符串内的索引,从指定的索引开始。
    返回的索引是最小的值k,其中:
    k>=fromIndex&amp;&amp;this。startsWith(str,k)
    如果不存在这样的k值,则返回-1。
    参数:
    str–要搜索的子字符串。
    fromIndex–开始搜索的索引。
    返回:指定子字符串第一次出现的索引,从指定的索引开始,如果没有出现,则为-1。
*/
public int indexOf(String str, int fromIndex) {
    return indexOf(value, 0, value.length,
                   str.value, 0, str.value.length, fromIndex);
}

2.4 int indexOf(String str)

源码:

/**
    返回指定子字符串第一次出现时该字符串内的索引。
    返回的索引是最小的值k,其中:
    这startsWith(str,k)
    如果不存在这样的k值,则返回-1。
    参数:str–要搜索的子字符串。
    返回:指定子字符串第一次出现的索引,如果没有出现,则为-1。
*/
public int indexOf(String str) {
    return indexOf(str, 0);
}

2.5 boolean contains(CharSequence s)

源码:

/**
    当且仅当此字符串包含指定的字符值序列时,返回true。
    参数:s–要搜索的序列
    返回:如果此字符串包含s,则为true,否则为false
    Since:1.5
*/
public boolean contains(CharSequence s) {
    return indexOf(s.toString()) > -1;
}

2.6 boolean startsWith(String prefix, int toffset)

源码:

/**
    测试此字符串中从指定索引开始的子字符串是否以指定前缀开始。
参数:
    prefix–前缀。
    toffset–从何处开始查看此字符串。
返回:
如果参数所表示的字符序列是从索引toffset开始的该对象的子字符串的前缀,则为true;否则就错了。
如果toffset为负值或大于该字符串对象的长度,则结果为false;否则,结果与表达式的结果相同
这子串(toffset)。startsWith(前缀)
*/
public boolean startsWith(String prefix, int toffset) {
    char ta[] = value;
    int to = toffset;
    char pa[] = prefix.value;
    int po = 0;
    int pc = prefix.value.length;
    // Note: toffset might be near -1>>>1.
    if ((toffset < 0) || (toffset > value.length - pc)) {
        return false;
    }
    while (--pc >= 0) {
        if (ta[to++] != pa[po++]) {
            return false;
        }
    }
    return true;
}

2.7 boolean startsWith(String prefix)

源码:

public boolean startsWith(String prefix) {
    return startsWith(prefix, 0);
}

2.8 boolean endsWith(String suffix)

源码:

public boolean endsWith(String suffix) {
    return startsWith(suffix, value.length - suffix.value.length);
}

2.9 boolean equals(Object anObject)

源码:

/**
    
*/
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;
}

2.10 boolean equalsIgnoreCase(String anotherString)

源码:

public boolean equalsIgnoreCase(String anotherString) {
    return (this == anotherString) ? true
        : (anotherString != null)
    && (anotherString.value.length == value.length)
        && regionMatches(true, 0, anotherString, 0, value.length);
}

2.11 char[] toCharArray()

2.12 String substring(int beginIndex)

从传入的索引处截取,截取到末尾,得到新的字符串

2.13 String substring(int beginIndex, int endIndex)

根据开始和结束索引进行截取,得到新的字 符串(包含头,不包含尾)

2.14 String replace(CharSequence target, CharSequence replacement)

使用新值,将字符串中 的旧值替换,得到新的字符串

2.15 String[] split(String regex)

根据传入的规则切割字符串,得到字符串数组

3. 常见面试题

Java 程序中所有的双引号字符串,都是 String 类的对象

字符串不可变,它们的值在创建后不能被更改
虽然 String 的值是不可变的,但是它们可以被共享

问题:下列代码的运行结果是?

public class Test1{
    public static void main(String[] args){
        String s1="abc";
        String s2="abc";  
        System.out.println(s1==s2);	
    }
}

分析:都在常量池中

问题:下列代码的运行结果是?

public class Test2{
    public static void main(String[] args){
        String s1="abc";
        String s2=new String("abc"); //创建两个对象
        System.out.println(s1==s2);	
    }
}

分析: s1在常量池 , s2 在 堆内存中

问题:下列代码的运行结果是?

public class Test3 {
    public static void main(String[] args) { 
        String s1 = "abc";
        String s2 = "ab";
        String s3 = s2 + "c";
        
        System.out.println(s1 == s3);  
    }
}

字符串对象使用"+" 拼接 会先转成StringBuildder 在进行append方法 最后toString方法 转成String对象

问题:下列代码的运行结果是?

public class Test4 {
    public static void main(String[] args) { 
        String s1 = "abc";
        String s2 = "a" + "b" + "c";
        System.out.println(s1 == s2);
    }
}

分析

  • 常量优化机制
  • 常量与常量之间运算 直接算结果 再赋值

举例:

String s1 = "abc";
String s2 = "abc";
//	s1 == s2 // true
    
String s1 = "abc";
String s2 = new String("abc");
//	s1 == s2 // false
    
String s1 = "java";
String s2 = "jav";
String s3 = s2 + "a"; // 变量 + 常量
//	s1 == s3 // false 字符串之间使用+号拼接的时候,系统底层会自创建一个StringBuilder对
//象,拼接后再转String

String s1 = "java";
String s2 = "jav" + "a"; //常量 + 常量
//	s1 == s2 // true 虽然是+拼接,但是是常量在拼接,常量优化机制

4. 深入理解

:::info
在Java语言中,所有类似“ABC”的字面值,都是String类的实例;String类位于java.lang包下,是Java语言的核心类,提供了字符串的比较、查找、截取、大小写转换等操作;Java语言为“+”连接符(字符串连接符)以及对象转换为字符串提供了特殊的支持,字符串对象可以使用“+”连接其他对象。String类的部分源码如下:
:::

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    ...
}

从上面可以看出
1)String类被final关键字修饰,意味着String类不能被继承,并且它的成员方法都默认为final方法;字符串一旦创建就不能再修改。
2)String类实现了Serializable、CharSequence、 Comparable接口。
3)String实例的值是通过字符数组实现字符串存储的。



4.1“+”连接符

4.1.1“+”连接符的实现原理

Java语言为“+”连接符以及对象转换为字符串提供了特殊的支持,字符串对象可以使用“+”连接其他对象。其中字符串连接是通过 StringBuilder(或 StringBuffer)类及其append 方法实现的,对象转换为字符串是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。
我们可以通过反编译验证一下

/**
 * 测试代码
 */
public class Test {
    public static void main(String[] args) {
        int i = 10;
        String s = "abc";
        System.out.println(s + i);
    }
}

/**
 * 反编译后
 */
public class Test {
    public static void main(String args[]) {    //删除了默认构造函数和字节码
        byte byte0 = 10;      
        String s = "abc";      
        System.out.println((new StringBuilder()).append(s).append(byte0).toString());
    }
}

由上可以看出,Java中使用"+"连接字符串对象时,会创建一个StringBuilder()对象,并调用append()方法将数据拼接,最后调用toString()方法返回拼接好的字符串。由于append()方法的各种重载形式会调用String.valueOf方法,所以我们可以认为:

//以下两者是等价的
s = i + ""
s = String.valueOf(i);
 
//以下两者也是等价的
s = "abc" + i;
s = new StringBuilder("abc").append(i).toString();

4.1.2“+”连接符的效率

使用“+”连接符时,JVM会隐式创建StringBuilder对象,这种方式在大部分情况下并不会造成效率的损失,不过在进行大量循环拼接字符串时则需要注意。

String s = "abc";
for (int i=0; i<10000; i++) {
    s += "abc";
}

/**
 * 反编译后
 */
String s = "abc";
for(int i = 0; i < 1000; i++) {
     s = (new StringBuilder()).append(s).append("abc").toString();    
}

这样由于大量StringBuilder创建在堆内存中,肯定会造成效率的损失,所以在这种情况下建议在循环体外创建一个StringBuilder对象调用append()方法手动拼接(如上面例子如果使用手动拼接运行时间将缩小到1/200左右)。

/**
 * 循环中使用StringBuilder代替“+”连接符
 */
StringBuilder sb = new StringBuilder("abc");
for (int i = 0; i < 1000; i++) {
    sb.append("abc");
}
sb.toString();

与此之外还有一种特殊情况,也就是当"+"两端均为编译期确定的字符串常量时,编译器会进行相应的优化,直接将两个字符串常量拼接好,例如:

System.out.println("Hello" + "World");

/**
 * 反编译后
 */
System.out.println("HelloWorld");
/**
 * 编译期确定
 * 对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。
 * 所以此时的"a" + s1和"a" + "b"效果是一样的。故结果为true。
 */
String s0 = "ab"; 
final String s1 = "b"; 
String s2 = "a" + s1;  
System.out.println((s0 == s2)); //result = true
/**
 * 编译期无法确定
 * 这里面虽然将s1用final修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定
 * 因此s0和s2指向的不是同一个对象,故上面程序的结果为false。
 */
String s0 = "ab"; 
final String s1 = getS1(); 
String s2 = "a" + s1; 
System.out.println((s0 == s2)); //result = false 
 
public String getS1() {  
    return "b";   
}

综上,“+”连接符对于直接相加的字符串常量效率很高,因为在编译期间便确定了它的值,也就是说形如"I"+“love”+“java”; 的字符串相加,在编译期间便被优化成了"Ilovejava"。对于间接相加(即包含字符串引用,且编译期无法确定值的),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。


4.2 字符串常量池

在Java的内存分配中,总共3种常量池,分别是 Class常量池、运行时常量池、字符串常量池。
字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性,常量池中一定不存在两个相同的字符串。

/**
 * 字符串常量池中的字符串只存在一份!
 * 运行结果为true
 */
String s1 = "hello world!";
String s2 = "hello world!";
System.out.println(s1 == s2);

4.2.1 内存区域

在HotSpot VM中字符串常量池是通过一个StringTable类实现的,它是一个Hash表,默认值大小长度是1009;这个StringTable在每个HotSpot VM的实例中只有一份,被所有的类共享;字符串常量由一个一个字符组成,放在了StringTable上。要注意的是,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。

在JDK6及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中的,StringTable的长度是固定的1009;在JDK7版本中,字符串常量池被移到了堆中,StringTable的长度可以通过**-XX:StringTableSize=66666**参数指定。至于JDK7为什么把常量池移动到堆上实现,原因可能是由于方法区的内存空间太小且不方便扩展,而堆的内存空间比较大且扩展方便。

4.2.2 存放的内容

在JDK6及之前版本中,String Pool里放的都是字符串常量;在JDK7.0中,由于String.intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用。

String s1 = "AB";
String s2 = "AB";
String s3 = new String("AB");
System.out.println(s1 == s2);
System.out.println(s1 == s3);

由于常量池中不存在两个相同的对象,所以s1和s2都是指向JVM字符串常量池中的"AB"对象。new关键字一定会产生一个对象,并且这个对象存储在堆中。所以String s3 = new String(“AB”);产生了两个对象:保存在栈中的s3和保存堆中的String对象。

当执行String s1 = "AB"时,JVM首先会去字符串常量池中检查是否存在"AB"对象,如果不存在,则在字符串常量池中创建"AB"对象,并将"AB"对象的地址返回给s1;如果存在,则不创建任何对象,直接将字符串常量池中"AB"对象的地址返回给s1。

4.2.3 intern 方法

直接使用双引号声明出来的String对象会直接存储在字符串常量池中,如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法是一个native方法,intern方法会从字符串常量池中查询当前字符串是否存在,如果存在,就直接返回当前字符串;如果不存在就会将当前字符串放入常量池中,之后再返回。
JDK1.7的改动:

  1. 将String常量池 从 Perm 区移动到了 Java Heap区
  2. String.intern() 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象。

intern的用法

static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];

public static void main(String[] args) throws Exception {
    Integer[] DB_DATA = new Integer[10];
    Random random = new Random(10 * 10000);
    for (int i = 0; i < DB_DATA.length; i++) {
        DB_DATA[i] = random.nextInt();
    }
    long t = System.currentTimeMillis();
    for (int i = 0; i < MAX; i++) {
        //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
         arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
    }

    System.out.println((System.currentTimeMillis() - t) + "ms");
    System.gc();
}

未使用intern,耗时 826 ms
使用intern,耗时 2160 ms
通过上述结果,我们发现不使用 intern 的代码生成了1000w 个字符串,占用了大约640m 空间。 使用了 intern 的代码生成了1345个字符串,占用总空间 133k 左右。其实通过观察程序中只是用到了10个字符串,所以准确计算后应该是正好相差100w 倍。虽然例子有些极端,但确实能准确反应出 intern 使用后产生的巨大空间节省。

细心的同学会发现使用了 intern 方法后时间上有了一些增长。这是因为程序中每次都是用了 new String 后,然后又进行 intern 操作的耗时时间,这一点如果在内存空间充足的情况下确实是无法避免的,但我们平时使用时,内存空间肯定不是无限大的,不使用 intern 占用空间导致 jvm 垃圾回收的时间是要远远大于这点时间的。 毕竟这里使用了1000w次intern 才多出来1秒钟多的时间。


5. String、StringBuilder和StringBuffer

5.1 继承结构

5.2 主要区别

1)String是不可变字符序列,StringBuilder和StringBuffer是可变字符序列。
2)执行速度StringBuilder > StringBuffer > String。
3)StringBuilder是非线程安全的,StringBuffer是线程安全的

6. 总结

public static void main(String[] args) {
    String s1 = "AB";
    String s2 = new String("AB");
    String s3 = "A";
    String s4 = "B";
    String s5 = "A" + "B";
    String s6 = s3 + s4;
    System.out.println(s1 == s2);
    System.out.println(s1 == s5);
    System.out.println(s1 == s6);
    System.out.println(s1 == s6.intern());
    System.out.println(s2 == s2.intern());
}

解析:真正理解此题目需要清楚以下三点
1)直接使用双引号声明出来的String对象会直接存储在常量池中;
2)String对象的intern方法会得到字符串对象在常量池中对应的引用,如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
3) 字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap -c命令获得class文件对应的JVM字节码指令就可以看出来。

运行结果:

7. 注意:

字符串 的比较 应该 使用 equals() 方法。

8. 参考文献

https://docs.oracle.com/javase/8/docs/api/
https://blog.csdn.net/sinat_19425927/article/details/38663461
https://www.cnblogs.com/xiaoxi/p/6036701.html
https://tech.meituan.com/in_depth_understanding_string_intern.html



在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/588819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python教学】Python兼职有哪些?给你们分享一下最适合学生党/工作党的Python兼职攻略以及接私活经验

文章目录 前言一、做兼职的优势二、兼职种类三、基本技能要求四、平台和渠道五、案例分析六、做兼职注意事项总结 前言 Python是一种高级编程语言&#xff0c;它具有简单易学、代码可读性高、功能强大等特点&#xff0c;被广泛应用于数据分析、人工智能、Web开发等领域。Pytho…

修改mysql密码与mac中mysql的启动与终止

目录 修改mysql密码 1.进入你的mysql文件下的bin目录下&#xff1a; 2.修改mysql密码 mysql的启动与终止&#xff08;mac&#xff09; 修改mysql密码 1.进入你的mysql文件下的bin目录下&#xff1a; 如果不知道自己电脑上的mysql在哪里的话&#xff0c;输入&#xff1a; …

Redis7实战加面试题-高阶篇(布隆过滤器BloomFilter,缓存预热+缓存雪崩+缓存击穿+缓存穿透)

布隆过滤器BloomFilter 先看看大厂真实需求面试题反馈 1.现有50亿个电话号码&#xff0c;现有10万个电话号码&#xff0c;如何要快速准确的判断这些电话号码是否已经存在? 2.判断是否存在&#xff0c;布隆过滤器了解过吗? 3.安全连接网址&#xff0c;全球数10亿的网址判断 …

Java 多线程共享数据引发的问题

一、多线程并发情况下&#xff0c;线程不安全​​ 1、使用多线程实现银行取钱​​ package theads;/*** ClassName: TestBank* Description: TODO* Author: HLX* date: 2023/5/29 14:53* Version: V1.0*//*** 线程不安全&#xff1a; 取钱* <p>* 逻辑&#xff1a;* 连取…

更改测试用例执行顺序的几种自动化方法

前言 在自动化测试中&#xff0c;自动化测试用例设计原则就是执行过程时不能存在依赖顺序&#xff0c;那么如果测试用例需要按照指定顺序执行&#xff0c;这个时候应该怎么做呢&#xff1f;目前单元测试框架中unittest没有办法改变测试用例的执行顺序&#xff0c;但是另一个单…

一顿操作,我成为了年薪40W+的测试开发,人麻了...

前情提要 我的第一份工作就是拿的8000多&#xff0c;主要以功能测试为主。我用了大概6年的时间&#xff0c;成为了年薪40W的测试开发。回顾我从功能测试到测试开发的成长路径&#xff0c;基本上是伴随着“3次能力飞跃”实现的。 第一家入职的时候是一家小公司 刚开始入行的时…

发改委强化电力需求侧管理,缓解电力系统峰值压力

安科瑞 耿敏花 摘要&#xff1a;近年来全国用电负荷特别是居民用电负荷的快速增长&#xff0c;全国范围内夏季、冬季用电负荷“双峰”特征日益突出&#xff0c;恶劣气候现象多发增加了电力安全供应的压力。具有随机性、波动性、间歇性特征的可再生能源大规模接入电网对电力系统…

WebrtcNode publish 流程

WebrtcNode publish 流程 1. AmqpClient - RpcServer New message received AmqpClient - RpcServer New message received {method: publish,args: [67f9309ce6e645fc8a4bb9cac6406eb2,webrtc,{transportId: 67f9309ce6e645fc8a4bb9cac6406eb2,tracks: [Array],controller: …

Spring注解开发——bean的作用范围与生命周期管理

文章目录 1.bean管理1.1 bean作用范围Scope注解 1.2 bean生命周期PostConstructPreDestroy 2.小结 1.bean管理 1.1 bean作用范围 Scope注解 不写或者添加Scope(“singleton”)表示的是单例 如何配置多例&#xff1f; 在Scope(“prototype”)表示的是多例 1.2 bean生命周…

chatgpt赋能python:Python中的倒序函数

Python中的倒序函数 Python是一种现代编程语言&#xff0c;它在不断地扩展和更新&#xff0c;使得它在编程领域中变得越来越流行。Python的一个特点是&#xff0c;代码简洁而且易于理解。其中一个重要的特性是它的倒序函数。在本文中&#xff0c;我们将介绍Python中的倒序函数…

一位年薪40W的测试被开除,回怼的一番话,令人沉思

一位年薪40W测试工程师被开除回怼道&#xff1a;“反正我有技术&#xff0c;在哪不一样” 一技傍身&#xff0c;万事不愁&#xff0c;当我们掌握了一技之长后&#xff0c;在职场上说话就硬气了许多&#xff0c;不用担心被炒&#xff0c;反过来还可以炒了老板&#xff0c;这一点…

基于深度学习的人脸识别与人员信息管理软件【python源码+UI界面+功能源码详解】

人脸识别功能演示 摘要&#xff1a;人脸识别&#xff08;Face Recognition&#xff09;是基于人的脸部特征信息进行身份识别的一种生物识别技术&#xff0c;可以用来确认用户身份。本文详细介绍了人脸识别基本的实现原理&#xff0c;并且基于python与pyqt开发了人脸识别与信息管…

智哪儿专访IF ROOM品牌主理人Amy:IF ROOM如何定义未来家居?

作为未来主义家居首倡者&#xff0c;IF ROOM在5月25日-28日举行的深圳时尚家居设计周上首次亮相就获得行业广泛热议。IF ROOM展馆以其未来主义设计风格和跨越东西方文化的美的意象&#xff0c;得到了包括设计师在内的广大行业人士追捧&#xff0c;IF ROOM展位成为深圳时尚家居设…

​字创未来 方正字库第十二届“方正奖”设计大赛正式来袭

传承汉字文化精髓&#xff0c;方正字库在字体行业不断探索深耕。方正字库一直致力于弘扬中华汉字文化&#xff0c;不断促进行业字体设计创新发展。于2001年在行业最艰难的时候&#xff0c;怀揣着对字体设计未来的美好向往&#xff0c;首届“北大方正奖”印刷字体设计大赛&#…

灵活使用Postman环境变量和全局变量,提高接口测试效率!

目录 前言&#xff1a; 环境变量和全局变量的概念 环境变量和全局变量的使用方法 1. 定义变量 2. 使用变量 环境变量和全局变量的实例代码 变量的继承和覆盖 变量的动态设置 总结&#xff1a; 前言&#xff1a; Postman是一个流行的API开发和接口测试工具&#xff0c;…

过滤器Filter,拦截器Interceptor

过滤器Filter 快速入门 详情 登录校验-Filter package com.itheima.filter;import com.alibaba.fastjson.JSONObject; import com.itheima.pojo.Result; import com.itheima.utils.JwtUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils…

码住!IC设计常用工具合集!

芯片设计过程中&#xff0c;选择和使用适合的工具是非常重要的。芯片设计工具通常分为三类&#xff1a;EDA工具、模拟仿真工具和布局工具。 一、EDA工具 EDA工具是芯片设计的核心&#xff0c;它包括原理图绘制、逻辑综合、门级仿真工具和物理版图编辑等&#xff0c;可以帮助设计…

Ubuntu 安装 jdk1.8

1 更新软件包 sudo apt update 2 安装jdk1.8 sudo apt install openjdk-8-jdk 3 查看版本 java -version安装成功

自适应滤波方法——LMS算法

自适应滤波器 自适应滤波器&#xff1a;一种能够根据输入信号自动调整自身参数的数字滤波器 非自适应滤波器&#xff1a;具有静态滤波器系统的数字滤波器&#xff0c;静态系数构成了滤波器的传递函数 对于一些应用&#xff08;如系统辨识、预测、去噪等&#xff09;&#xff…