常用数据结构之String字符串

字符串

在Java编程语言中,字符可以使用基本数据类型char来保存,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

操作字符串常用的有三种类:String、StringBuilder、StringBuffer

接下来看看这三类常见用法

String

String value = new String("测试下");
System.out.println(value);

特点:

不可变

String value_a = "测试下原来值";value_a = "改变了";System.out.println(value_a);

实际打印

改变了

看起来好像改变了,那string为啥还具有不可变的特点呢

那咱来看看以上程序执行过程中value_a的指向

可以看到原来value_a指向“测试下原来值”字符串,后来有指向了“改变了”

为啥String不可变,咱进入String源码看看咋实现的

public final class Stringimplements 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/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = -6849794470754667710L;/*** Class String is special cased within the Serialization Stream Protocol.** A String instance is written into an ObjectOutputStream according to* <a href="{@docRoot}/../platform/serialization/spec/output.html">* Object Serialization Specification, Section 6.2, "Stream Elements"</a>*/private static final ObjectStreamField[] serialPersistentFields =new ObjectStreamField[0];/*** Initializes a newly created {@code String} object so that it represents* an empty character sequence.  Note that use of this constructor is* unnecessary since Strings are immutable.*/public String() {this.value = "".value;}/*** Initializes a newly created {@code String} object so that it represents* the same sequence of characters as the argument; in other words, the* newly created string is a copy of the argument string. Unless an* explicit copy of {@code original} is needed, use of this constructor is* unnecessary since Strings are immutable.** @param  original*         A {@code String}*/public String(String original) {this.value = original.value;this.hash = original.hash;}/*** Allocates a new {@code String} so that it represents the sequence of* characters currently contained in the character array argument. The* contents of the character array are copied; subsequent modification of* the character array does not affect the newly created string.** @param  value*         The initial value of the string*/public String(char value[]) {this.value = Arrays.copyOf(value, value.length);}/*** Allocates a new {@code String} that contains characters from a subarray* of the character array argument. The {@code offset} argument is the* index of the first character of the subarray and the {@code count}* argument specifies the length of the subarray. The contents of the* subarray are copied; subsequent modification of the character array does* not affect the newly created string.** @param  value*         Array that is the source of characters** @param  offset*         The initial offset** @param  count*         The length** @throws  IndexOutOfBoundsException*          If the {@code offset} and {@code count} arguments index*          characters outside the bounds of the {@code value} array*/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);}/*** Allocates a new {@code String} that contains characters from a subarray* of the <a href="Character.html#unicode">Unicode code point</a> array* argument.  The {@code offset} argument is the index of the first code* point of the subarray and the {@code count} argument specifies the* length of the subarray.  The contents of the subarray are converted to* {@code char}s; subsequent modification of the {@code int} array does not* affect the newly created string.** @param  codePoints*         Array that is the source of Unicode code points** @param  offset*         The initial offset** @param  count*         The length** @throws  IllegalArgumentException*          If any invalid Unicode code point is found in {@code*          codePoints}** @throws  IndexOutOfBoundsException*          If the {@code offset} and {@code count} arguments index*          characters outside the bounds of the {@code codePoints} array** @since  1.5*/public String(int[] codePoints, int offset, int count) {if (offset < 0) {throw new StringIndexOutOfBoundsException(offset);}if (count <= 0) {if (count < 0) {throw new StringIndexOutOfBoundsException(count);}if (offset <= codePoints.length) {this.value = "".value;return;}}// Note: offset or count might be near -1>>>1.if (offset > codePoints.length - count) {throw new StringIndexOutOfBoundsException(offset + count);}final int end = offset + count;// Pass 1: Compute precise size of char[]int n = count;for (int i = offset; i < end; i++) {int c = codePoints[i];if (Character.isBmpCodePoint(c))continue;else if (Character.isValidCodePoint(c))n++;else throw new IllegalArgumentException(Integer.toString(c));}// Pass 2: Allocate and fill in char[]final char[] v = new char[n];for (int i = offset, j = 0; i < end; i++, j++) {int c = codePoints[i];if (Character.isBmpCodePoint(c))v[j] = (char)c;elseCharacter.toSurrogates(c, v, j++);}this.value = v;}
}

可以看到String类有个final修饰符,final修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的

相当于string类型指向的具体内容不会变,例如刚才的例子,

value_a = "测试下原来值"; 这个数值不会变,原本有这个字符串有char value[]来进行保存本质不会被修改
value_a = "改变了"; 后续改变value_a的数值,会把原本value_a的引用指向新的字符串"改变了",而不是改变原来有char value[]保存的"测试下原来值",但由于无引用指向原来的字符串"测试下原来值",会被垃圾回收掉

结论:String不可变指的是String的内容不会变,但是可以改变String引用指向新的字符串

接下来看个🌰

String pre = "Hello,World";String new_value = "Hello,World";String object_value = new String("Hello,World");System.out.println(object_value.hashCode());System.out.println(pre.hashCode());System.out.println(new_value.hashCode());System.out.print("通过==判断pre是否与new_value相等->");System.out.println(pre==new_value);System.out.print("通过==判断pre是否与object_value相等->");System.out.println(pre==object_value);System.out.println("----------------");System.out.print("通过.equals判断pre是否与new_value相等->");System.out.println(pre.equals(new_value));System.out.print("通过.equals判断pre是否与object_value相等->");System.out.println(pre.equals(object_value));

以上程序分别会打印true还是false呢

可以看到通过字面创建的"Hello,World",不管新建多少次,==判断和equals判断都相等

通过对象创建的"Hello,World"和字面量新建的字符串通过==判断也相等,但是通过equals判断对象创建和字面量创建返回的false

首先来看看equals和==的区别

equals与==区别

==:判断两个对象的地址是否相等

equals:Object超级父类有个equals方法

    public boolean equals(Object obj) {return (this == obj);}

Object是直接判断两者地址是否相同,与==作用相同

而且所有的对象都会继承Object类

可以看看官方的解释

package java.lang;/*** Class {@code Object} is the root of the class hierarchy.* Every class has {@code Object} as a superclass. All objects,* including arrays, implement the methods of this class.*类对象是类层次结构的根。每个类都有Object作为超类。所有对象,包括数组,都实现了此类的方法。* @author  unascribed* @see     java.lang.Class* @since   JDK1.0*/
public class Object

所以String也继承了Object类,具有equals方法

来看看String的equals的方法咋实现的呢

public boolean equals(Object anObject) {if (this == anObject) {return true;//假设两个对象地址相同==,则返回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) {//循环遍历String的value数组if (v1[i] != v2[i])return false;//其中有任何一个不同,认定两者不同i++;}return true; //所有字符都相同的情况下,相当于两者相同}}return false; //把不是String类与String对比 相当于两者不同}

可以看出来String的equals方法

1,假设两个对象地址相同==,则返回true 相当于两者相同

2,判断对象是否属于string

3,循环遍历String的value数组,其中有任何一个不同,认定两者不同

4,所有字符都相同的情况下,相当于两者相同

5,不是String类与String对比 相当于两者不同

而Object是直接判断两者地址是否相同

所以再看看之前举的🌰

String pre = "Hello,World";String new_value = "Hello,World";String object_value = new String("Hello,World");System.out.println(object_value.hashCode());System.out.println(pre.hashCode());System.out.println(new_value.hashCode());System.out.print("通过==判断pre是否与new_value相等->");System.out.println(pre==new_value);System.out.print("通过==判断pre是否与object_value相等->");System.out.println(pre==object_value);System.out.println("----------------");System.out.print("通过.equals判断pre是否与new_value相等->");System.out.println(pre.equals(new_value));System.out.print("通过.equals判断pre是否与object_value相等->");System.out.println(pre.equals(object_value));

pre和new_value都是通过字面量的形式创建的字符串,"Hello,World"字符串会保存到常量池当中,新建了pre引用变量和new_value引用变量指向的同一个"Hello,World"对象,所以两者本质属于同个对象,所以==和equals都相同

object_value在堆中新建了对象,object_value引用变量指向堆中的"Hello,World"对象

所以pre==object_value会返回false,两者属于两个对象

pre.equals(new_value)返回true,由于String对equals重写了,只需两者都是String对象,且value数组的值都相同则返回true

hashcode

再看看为啥这三者的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()方法‌:用于返回对象的哈希码,这是一个整数值。哈希码用于确定对象在哈希表中的索引位置。理想情况下,不同的对象应该产生不同的哈希码,但这不是强制的。哈希码的计算通常基于对象的属性,不同的对象可能产生相同的哈希码,这就是所谓的哈希冲突。

上述hashcode实现简化成:

value[n],字符数组有n个字符 计算规则顺序 定义h[n]为当前遍历计算出来的值

n=0   h[0]=value[0]

n=1:  h[1]=h[0]*31+value[1]

n=2: h[2] = h[1]*31+value[2]=(value[0]*31+value1)*31+value[2]

选择 31原因:

优化计算‌:31可以被编译器优化为31 * i = (i << 5) - i,这种形式利用了位运算和减法,比直接的乘法运算效率更高‌

减少冲突‌:31是一个质数,使用质数作为乘法因子可以降低哈希冲突的概率。质数的乘法性质使得每个字符的哈希值与31相乘时,都能保持相对独立的哈希值,从而减少冲突的可能性‌

31用2进制来表达为 11111 ,31*i等价于(i<<5)-i,即先将i左移5位(相当于乘以32),然后再减去i

注意点:

1,两者equals相同,则两者的hashcode返回整数值相同

2,若两者hashcode相同,equals不一定相同

3,不同的对象hashcode,为了使得减少哈希表的冲突,尽量保持不同

可以看到pre、new_value、object_value三者的value数组保存的数值相同都是"Hello,World"

所以通过hash算法算出来的索引为止即hash值相同

接下来再看个🌰

String a = "Hello";String b = "Hello";String c = new String("Hello");String e = a+b;String t = a+b+c;String l = a+"World";String l1 = "Hello"+World"

以上程序共新建了多少对象呢

a="Hello",一个对象

b由于"Hello"在字符串常量池中存在,

c 在堆中新建1个新的字符串常量对象"Hello",栈内存储c变量引用指向它

e 通过+来拼接字符串实际通过StringBuilder来构建的所有会新建个对象StringBuilder,

t a="Hello"在常量池中有,无需新建,b="Hello"在常量池中有,无需新建,c为堆对象引用,已新建过了,但是新的字符串"HelloHelloHello"会放入堆中新建个对象

l "World"和"Hello"会在编译期间拼接,新建"HelloWorld"对象,注意这里不会新建"World"对象进入常量池

l1 编译期间拼接成"HelloWorld" 新建1个对象 注意这里与l不同 l通过StringBuilder拼接的 两个对象部署同个对象

所以总共新建了6个对象

总结

字面量新建

String a = "Hello"; // 在字符串常量池中存入"Hello" 新建1个对象 a引用变量指向它
String b = "Hello";// 在字符串常量找到了"Hello" 无需新建对象 b引用变量指向它
String c = "World";// 在字符串常量池中找不到"World" 常量池中存入"World" 新建1个对象 c引用变量指向它

对象新建

String a = "Hello"; // 在字符串常量池中存入"Hello" 新建1个对象 a引用变量指向它
String b = "Hello";// 在字符串常量找到了"Hello" 无需新建对象 b引用变量指向它
String c = "World";// 在字符串常量池中找不到"World" 常量池中存入"World" 新建1个对象 c引用变量指向它
String t = new String("Hello");//"Hello"在字符串常量池中存在,无需在常量池在存入"Hello",在堆中新建对象指向它
String t1 = new String("HelloWorld");//"HelloWorld"在字符串常量池中不存在,在常量池在存入"HelloWorld",内存堆中新建对象指向它

+拼接

String a = "Hello"; // 在字符串常量池中存入"Hello" 新建1个对象 a引用变量指向它
String b = "Hello";// 在字符串常量找到了"Hello" 无需新建对象 b引用变量指向它
String c = "World";// 在字符串常量池中找不到"World" 常量池中存入"World" 新建1个对象 c引用变量指向它
String t = new String("Hello");//"Hello"在字符串常量池中存在,无需在常量池在存入"Hello",在堆中新建对象指向它
String t1 = new String("HelloWorld");//"HelloWorld"在字符串常量池中不存在,在常量池在存入"HelloWorld",内存堆中新建对象指向它
String b1 = "Hello"+"World";//"HelloWorld"在常量池中已有,所以无需新建对象
String new_value = "Hello"+"Java";//"HelloJava"在常量池中不存在,常量池新建"HelloJava"
String value_new = a+b;//通过StringBuilder拼接会新建个对象

拼接"Hello"常量:直接拼接,会在编译期间进入常量池

但不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以

  • 基本数据类型( bytebooleanshortcharintfloatlongdouble)以及字符串常量。
  • final 修饰的基本数据类型和字符串变量
  • 字符串通过 “+”拼接得到的字符串、基本数据类型之间算数运算(加减乘除)、基本数据类型的位运算(<<、>>、>>> )

引用的值在程序编译期是无法确定的,编译器无法对其进行优化

拼接常量引用变量:字符对象的拼接实际上底层是使用的StringBuilder的append方法,先将字符串对象转换成StringBuilder然后调用append方法之后再调用toString(),此时生成的是另一个String对象,String对象存储在堆中,不会存入常量池

注意:‌String().intern()方法在Java中的作用是将字符串对象添加到字符串常量池中,如果常量池中已经存在相同的字符串,则返回该字符串的引用;如果不存在,则创建一个新的字符串对象并添加到常量池中‌‌

对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。

注意被final修饰的常量的引用拼接可以直接在编译期间再进入常量池

但修饰1个当时无法确定的数值,即在运行时才可以确定的值,则海还是会通过StringBuilder来拼接

开发中应该减少多个字符串拼接操作

所以出现了StringBuilder和StringBuffer

StringBuilder

由于string为不可变的,后续又设计了stringbuilder类

‌StringBuilder是Java中的一个可变字符串操作类,主要用于在需要频繁修改字符串内容的场景下使用,以提高性能。

StringBuilder的优势和适用场景

  1. 性能优势‌:StringBuilder是可变的,对字符串的修改直接作用于当前对象,无需创建新对象,因此在需要频繁拼接或修改字符串时,性能更高‌1。
  2. 适用场景‌:适合在单线程环境下使用,特别是在本地应用程序或单线程任务中需要频繁修改字符串时,StringBuilder的性能优于StringBuilder

源码

public final class StringBuilderextends AbstractStringBuilderimplements java.io.Serializable, CharSequence
{/** use serialVersionUID for interoperability */static final long serialVersionUID = 4383685877147921099L;/*** Constructs a string builder with no characters in it and an* initial capacity of 16 characters.*/public StringBuilder() {super(16);}/*** Constructs a string builder with no characters in it and an* initial capacity specified by the {@code capacity} argument.** @param      capacity  the initial capacity.* @throws     NegativeArraySizeException  if the {@code capacity}*               argument is less than {@code 0}.*/public StringBuilder(int capacity) {super(capacity);}/*** Constructs a string builder initialized to the contents of the* specified string. The initial capacity of the string builder is* {@code 16} plus the length of the string argument.** @param   str   the initial contents of the buffer.*/public StringBuilder(String str) {super(str.length() + 16);append(str);}/*** Constructs a string builder that contains the same characters* as the specified {@code CharSequence}. The initial capacity of* the string builder is {@code 16} plus the length of the* {@code CharSequence} argument.** @param      seq   the sequence to copy.*/public StringBuilder(CharSequence seq) {this(seq.length() + 16);append(seq);}@Overridepublic StringBuilder append(Object obj) {return append(String.valueOf(obj));}@Overridepublic StringBuilder append(String str) {super.append(str);return this;}/*** Appends the specified {@code StringBuffer} to this sequence.* <p>* The characters of the {@code StringBuffer} argument are appended,* in order, to this sequence, increasing the* length of this sequence by the length of the argument.* If {@code sb} is {@code null}, then the four characters* {@code "null"} are appended to this sequence.* <p>* Let <i>n</i> be the length of this character sequence just prior to* execution of the {@code append} method. Then the character at index* <i>k</i> in the new character sequence is equal to the character at* index <i>k</i> in the old character sequence, if <i>k</i> is less than* <i>n</i>; otherwise, it is equal to the character at index <i>k-n</i>* in the argument {@code sb}.** @param   sb   the {@code StringBuffer} to append.* @return  a reference to this object.*/public StringBuilder append(StringBuffer sb) {super.append(sb);return this;}@Overridepublic StringBuilder append(CharSequence s) {super.append(s);return this;}
}

可以看出来构造函数有4个

StringBuilder():字符初始容量为16的StringBuilder

StringBuilder(int capacity):字符初始容量为指定数量的StringBuilder

StringBuilder(CharSequence seq):包含与指定的CharSequence相同的字符序列

StringBuilder(String str):包含与指定的String相同的字符序列

常用方法:

append():

public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;}

可以看出来返回的对象是this,不会增加新的对象,对比String内存占用少了很多

insert():

public AbstractStringBuilder insert(int offset, char[] str) {if ((offset < 0) || (offset > length()))throw new StringIndexOutOfBoundsException(offset);int len = str.length;ensureCapacityInternal(count + len);System.arraycopy(value, offset, value, offset + len, count - offset);System.arraycopy(str, 0, value, offset, len);count += len;return this;}

可以看出来返回的对象是this,不会增加新的对象,对比String内存占用少了很多

toString():

@Overridepublic String toString() {// Create a copy, don't share the arrayreturn new String(value, 0, count);}

StringBuilder类不是线程安全的,有多个线程同时对同一个StringBuilder对象进行操作,可能会出现并发问题。

StringBuffer

线程安全的,主要原因在于其内部方法关键字进行同步‌。这意味着多个线程可以安全地同时访问和修改同一个StringBuffer对象,而不会导致数据不一致或其他线程相关的问题‌

@Overridepublic synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this;}

synchronized即同步锁,当前线程执行情况下,其它线程会同步等待直至当前线程释放锁

    

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wordpressAI工具,已接入Deepseek 支持自动生成文章、生成图片、生成长尾关键词、前端AI窗口互动、批量采集等

基于关键词或现有内容生成SEO优化的文章&#xff0c;支持多种AI服务&#xff08;如OpenAI、百度文心一言、智谱AI等&#xff09;&#xff0c;并提供定时任务、内容采集、关键词生成等功能。 核心功能 文章生成 关键词生成&#xff1a;根据输入的关键词生成高质量文章。 内容…

Vite 为什么快,是怎么打包的

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

协议-ACLLite-ffmpeg

是什么&#xff1f; FFmpeg是一个开源的多媒体处理工具包&#xff0c;它集成了多种功能&#xff0c;包括音视频的录制、转换和流式传输处理。FFmpeg由一系列的库和工具组成&#xff0c;其中最核心的是libavcodec和libavformat库。 libavcodec是一个领先的音频/视频编解码器库&…

【C++高并发服务器WebServer】-12:TCP详解及实现

本文目录 一、TCP通信流程二、套接字函数2.1 socket()2.2 bind()2.3 listen()2.4 accept()2.5 connect() 三、demo实现3.1 server端代码3.2 client端代码 四、TCP三次握手五、TCP滑动窗口六、TCP四次挥手七、多进程并发服务器 一、TCP通信流程 先来讲讲服务器端&#xff0c;是…

【Maven】项目管理工具-Maven

目录 1. Maven简介 1.1 项目管理 1.2 项目构建 1.3 项目构建工具 1.4 Maven的四大特征 1.4.1 依赖管理系统 1.4.2 多模块构建 1.4.3 一致的项目结构 1.4.4 一致的构建模型和插件机制 1.5 Maven模型 ​编辑 2.maven的安装配置 2.1 Maven的安装配置 2.1.1检测jdk的版…

dijkstra算法类型题解

dijkstra算法&#xff08;有权图&#xff0c;无权图&#xff09;&#xff1a; 带权路径长度——当图是带权图时&#xff0c;一条路径上所有边的权值之和&#xff0c;称为该路径的带权路径长度 初始化三个数组&#xff0c;final标记各顶点是否已找到最短路径&#xff0c;dist最…

RabbitMQ 消息顺序性保证

方式一&#xff1a;Consumer设置exclusive 注意条件 作用于basic.consume不支持quorum queue 当同时有A、B两个消费者调用basic.consume方法消费&#xff0c;并将exclusive设置为true时&#xff0c;第二个消费者会抛出异常&#xff1a; com.rabbitmq.client.AlreadyClosedEx…

基于开源AI智能名片2+1链动模式S2B2C商城小程序的个人IP活动运营策略与影响力提升研究

摘要&#xff1a;本文围绕个人IP运营者借助活动运营提升影响力这一主题&#xff0c;深入探讨如何将开源AI智能名片21链动模式S2B2C商城小程序融入借势、造势、提升参与感及用户激励等活动运营环节。通过分析该创新模式与活动运营各要素的结合点&#xff0c;为个人IP运营者提供切…

计算机图形学论文 | 面向制造的设计: 五轴铣削的几何制造可行性评估

&#x1f355;&#x1f355;&#x1f355;宝子们好久不见&#xff0c;新年快乐~~~&#xff0c;今天我们来更新一篇关于五轴CNC制造中的模型制造可达性分析的论文。老规矩&#xff1a; 红色是名词&#xff0c;蓝色是结论&#xff0c;绿色是文章工作&#xff0c;黄色是一些其他重…

deepseek搭建本地知识库

ollama是一个大模型的运行框架&#xff0c;在上面可以运行不同的大模型 部署deepseek 下载ollama&#xff1a;https://ollama.com/ 下载模型&#xff1a;https://ollama.com/library/deepseek-r1:1.5b ollama run deepseek-r1:1.5b运行起来之后&#xff0c;本地命令行就可以…

青少年编程与数学 02-009 Django 5 Web 编程 01课题、概要

青少年编程与数学 02-009 Django 5 Web 编程 01课题、概要 一、Django 5Django 5 的主要特性包括&#xff1a; 二、MVT模式三、官方网站四、内置功能数据库 ORM&#xff08;对象关系映射&#xff09;用户认证和授权表单处理模板引擎URL 路由缓存框架国际化和本地化安全性功能管…

deepseek本地部署-linux

1、官网推荐安装方法(使用脚本,我绕不过github,未采用) 登录ollama下载网站https://ollama.com/download/linux,linux下有下载脚本。 正常来说,在OS系统下直接执行脚本即可。 2、手动安装方法 2.1获取ollama-linux-arm64.tgz wget https://ollama.com/download/ollam…

多光谱技术在华为手机上的应用发展历史

2018 年&#xff0c;华为 P20 系列首次搭载 5 通道色温传感器&#xff0c;可帮助手机在不同光照条件下保持画面色彩一致性。 2020 年&#xff0c;华为 P40 系列搭载 8 通道多光谱色温传感器&#xff08;实际为 11 通道&#xff0c;当时只用 8 个通道检测可见光&#xff09;&am…

增加工作台菜单页面,AI问答应用支持上下文设置,数据库表索引优化,zyplayer-doc 2.4.8 发布啦!

zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;支持在线编辑富文本、Markdown、表格、Office文档、API接口、思维导图、Drawio以及任意的文本文件&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;支持以内…

4.python+flask+SQLAlchemy+达梦数据库

前提 1.liunx Centos7上通过docker部署了达梦数据库。从达梦官网下载的docker镜像。(可以参考前面的博文) 2.windows上通过下载x86,win64位的达梦数据库,只安装客户端,不安装服务端。从达梦官网下载达梦数据库windows版。(可以参考前面的博文) 这样就可以用windows的达…

基础入门-网站协议身份鉴权OAuth2安全Token令牌JWT值Authirization标头

知识点&#xff1a; 1、网站协议-http/https安全差异&#xff08;抓包&#xff09; 2、身份鉴权-HTTP头&OAuth2&JWT&Token 一、演示案例-网站协议-http&https-安全测试差异性 1、加密方式 HTTP&#xff1a;使用明文传输&#xff0c;数据在传输过程中可以被…

【零基础学Mysql】常用函数讲解,提升数据操作效率的利器

以耳倾听世间繁华&#xff0c;以语表达心中所想 大家好,我是whisperrrr. 前言&#xff1a; 大家好&#xff0c;我是你们的朋友whisrrr。在日常工作中&#xff0c;MySQL作为一款广泛使用的开源关系型数据库&#xff0c;其强大的功能为我们提供了便捷的数据存储和管理手段。而在…

C++ 使用CURL开源库实现Http/Https的get/post请求进行字串和文件传输

CURL开源库介绍 CURL 是一个功能强大的开源库&#xff0c;用于在各种平台上进行网络数据传输。它支持众多的网络协议&#xff0c;像 HTTP、HTTPS、FTP、SMTP 等&#xff0c;能让开发者方便地在程序里实现与远程服务器的通信。 CURL 可以在 Windows、Linux、macOS 等多种操作系…

win编译openssl

一、perl执行脚本 1、安装perl脚本 perl安装 2、配置perl脚本 perl Configure VC-WIN32 no-asm no-shared --prefixE:\openssl-x.x.x\install二、编译openssl 1、使用vs工具编译nmake 如果使用命令行nmake编译会提示“无法打开包括文件: “limits.h”“ 等错误信息 所以…

idea启动报错# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffccf76e433

# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc0x00007ffccf76e433, pid17288, tid6696 # # JRE version: (11.0.248) (build ) # Java VM: OpenJDK 64-Bit Server VM (11.0.248-LTS, mixed mode, sharing, tiered, compressed oops, g1 gc, windows-amd64) 不知道为什么…