java学习(3)String类分析

让## string和包装类

string基础

字符串广泛运用在java编程中,在java中字符串也属于对象,java提供了String类来
创建和操作字符串。
java中的字符串不是内置的类型,而是在标准java类库中提供一个自定义类
@创建字符串
创建字符串最简单的方式如下:
String greeting =“菜鸟教程”;
在代码中遇到字符串常量时,这里的值是"菜鸟教程",编译器会自动使用该值创建一个String对象。

和其他对象一样,可以使用关键字和创造方法来创建String对象。

和其他对象一样,可以使用关键字和构造方法来创建String对象。

String类有11种构造方法,这些方法提供不同的参数来初始字符串,比如提供一个字符数组参数:

@String Demo.java 文件代码

public class StringDemo{
public static void main(String 【】args){
char 【】helloArray = {‘r’,‘u’,‘n’,‘o’,‘o’,‘b’};
String helloString = new String(helloArray);
System.out.println(helloString)}
}

以上实例编译运行结果如下:
runoob

!!注意:String类对象是不可以改变的,所以一旦创建了String对象,那它就无法被改变值了(有些地方叫做不可变字符集)

如果要对字符串进行很多修改的操作,则应该选择StringBuffer 和StringBuilder类(其对象有些地方叫做可变字符集)

String基本用法

@创建String对象的常用方法
(1)String s1 = “mpptest”
(2)String s2 = new String();
(3)String s3 = new String(“mpptest”)

基本方法

@取长度:
int length();方法
该方法用于返回字符串对象的长度

@子串:
String类的substring方法可以从一个较大的字符串中提取出一个子串
例如:
String greeting = 'Hello";
String s = greeting.substring(0,3);
创建一个长度为3的子串(Hel
类似于C_C++,Java字符串中的代码单元和代码点从0开始计数
substring(a,b)获取从a位置到b位置的子串
substring (a)获取从a位置到结束的字符串
由工作机制可以很容易得出子串长度b-a;

@拼接
与大多数程序设计语言一样,jvav允许使用+号拼接两个字符串
String expletive = “Expletive”;
String PG13 = “deleted”;
String message = expletive +PG13;
上述代码的结果是将“Expletivedeleted”赋值给message(即无缝衔接)

当将一个字符串和非字符串对象试图拼接在一起时,后者会被转换为字符串
(任何一个对象都可以转换为字符串形式,在后面学习中会了解到)

例如:
int age = 13;
String rating = "PG"+age;
将rating设置为“PG13”
这种特性常被用在输出语句上,例如:
System.out.println(“the answer is ” + answer);
因为is后设置了一个空格,实际answer的数值是在空格之后开始的

如果需要把多个字符串放在一起,用一个定界符分离,则可以使用静态join方法
String all = String join ("/",“S”,“M”,“L”,“XL”);
(S/M/L/XL)(用第一个参数作为定界符?

java11中提供了一个repeat方法:
String repeated = “java”.repeat(3);//repeated is “javajavajava”
实际上就是一个重复n次的复读机。。。

@查找
indexOf()/lastIndexOf()
用法:
对于“ABCDEABCDE“”
字符串.indexOf(“A”)//查找子串"A"第一次出现的位置
indexOf(“AB”)//查找子串“AB”第一次出现的位置
indexOf(“A”,4)//从indexOf为4的位置开始找第一次出现的“A”
lastIndexOf则相反,是查找最后一次出现的位置
//后面的参数可以表示起始位置,而前面的参数若为int类型则·应该看成是对应的字符

!!!拓展:
@

  • 字符串与byte数组之间的相互转换
package com.mpp.string; 
import java.io.UnsupportedEncodingException;
public class StringDemo3 { 
public static void main(String[] args) throws UnsupportedEncodingException 
{ 
//字符串和byte数组之间的相互转换 
String str = new String("hhhabc银鞍照白马 飒沓如流星"); 
//将字符串转换为byte数组,并打印输出 
byte[] arrs = str.getBytes("GBK"); 
//使用GBK编码进行转换
for(int i=0;i){ 
System.out.print(arrs[i]); 
} 
//将byte数组转换成字符串 
System.out.println(); 
String str1 = new String(arrs,"GBK"); 
//保持字符集的一致,否则会出现乱码 
System.out.println(str1); 
} 
}

/*
*
*
*/@重点:字符串“相等”

==运算符和equals之间的区别

package com.mpp.string; public class StringDemo5 { public static void main(String[] args) {
        String str1 = "mpp";
        String str2 = "mpp";
        String str3 = new String("mpp");

        System.out.println(str1.equals(str2)); //true  内容相同
        System.out.println(str1.equals(str3));   //true  内容相同
        System.out.println(str1==str2);   //true   地址相同
        System.out.println(str1==str3);   //false  地址不同
 }
}

一般判断字符串“内容”是否相等的话不用==而用equals()
原因在于:==对于字符串相等的判断实际上是对地址是否相等的判断,但如上
例所展示的,同样内容的字符串完全有可能存放在不同的地址上!

内存:

在这里插入图片描述
由上图可知,实际上只有创建字符串时在常量池储存的字符字面量是共享地址的,而
new ,+ ,substring等操作形成的字符串并不共享。

【所以说String对象是不可变的,所谓的“变”其实是创建了新的对象或是新建的指向常量池的引用!!!】

回顾刚刚的连接:
@Test void contact (){
//1连接方式
String s1 =“a”;
String s2 = “a”;
String s3 = “a”+s2
String s4 = “a”+“a”;
String s5 = s1+s2;
//表达式只有常量时,编译期间完成计算
//表达式中有变量时,运行时计算,所以地址不一样
System.out.println(s3= =s4);//f
System.out.println(s3= =s5);//f
System.out.println(s4 = = “aa”);//t

总结:字符串加法对常量编译期间计算,对有变量表达式编译后运行时计算。

空串与null串

空串是长度为0的字符串“ ”
检测一个字符串是否为空的代码为:

if(str.length()==0

if (str.equals(“ ”))

空串是一个java对象,有自己的长度(0)和内容(空白符)。

String变量还可以存放一个特殊的值null,表示目前没有任何对象与该变量关联

if(str==null)

(null串表示没有一个实际的对象,所以也无法调用相应的方法,调用时会报错)

有时要检测一个字符串既不是空串也不是null串,这时候就要使用以下语句:

if(str !=null&&str.length()!=0

码点与代码单元

java字符串是由char值序列完成的,该数据了理性是一个采用UTF-16编码表示Unicode码点的代码单元。最常用的Unicode字符使用一个代码单元就可以表示,而辅助字符需要用一对代码单元表示。
length方法将返回采用UTF-16编码表示的字符串需要的代码单元数量
例如:

String greeting=“Hello”;
int n= greeting.length();//5

调用s.charAt(n)将返回n位置处的代码单元,n介于0和s.length()-1之间。
例如:

char first = greeting.charAt(0);//first is 'H'
char last = greeting.charAt(4);//last is 'o'

要想得到第i个码点,可以使用以下代码:

int index = greeting.offsetByCodePoints(0,i);
int cp = greeting.codePointAt(index);

因为使用Utf-16编码字符表示的一些码点实际占用两个代码单元

(增补类型使用两个char类型表示,第一个叫“高代理部分”,第二个“低代理部分”,
遇到高代理部分,向后读一个char类型,如果读出来是低代理部分,则将它们看成一个整体,否则返回第一个字符,将它们分别处理 )

如果想要遍历一个字符串,并且依次查看每个码点,可以使用以下语句:

int cp = sentence.codePointAt(i);
if(Character.isSupplementaryCodePoint(cp))i+=2; 

如果想反向遍历,应该使用以下语句:

i--;
if(Character.isSurrogate(sentence.charAt(i)))i--;
int cp = sentence.codePointAt(i)

显然,这是比较麻烦的一个流程,更简单点的方法是使用codePoints方法,它会生成一个
int值的“流”,每个int值对应一个码点。(流将在后面进行讨论)可以将它转换为一个数组,再完成遍历:

int 【】codePoints = str.codePoints().toArray();

反之,要将一个码点数组转换为一个字符串,则可以使用构造器

String str = new String(codePoints,0,codePoints.length);

虚拟机不一定把字符串实现为代码单元序列,在java9中,只包含单字节代码单元的字符串使用byte数组实现,而所有其他字符串使用char数组。

String 、String builder和String buffer的区别

String类是java中Immutable类的典型实现,除了hash属性其他都被声明为final(为了
其不可变性),这导致拼接的时候会出现很多的无用中间对象,如果频繁的这么操作会对性能有影响

StringBuffer类就是为了解决大量拼接字符串时中间对象问题而提供的类,提供
add和append方法,可以将字符串添加到已有序列的末尾或者指定位置,它的本质是一个线程安全的可以修改的字符序列,把所有修改数据的地方都加上了synchronized。但是保证线程安全会付出性能的代价

很多时候我们不需要线程安全来拼接字符串,于是出现了StringBuilder类

StringBuilder类和StringBuffer类实际上没有什么区别,只是将保证线程安全的部分去掉了而已。减少了开销

StringBuilder和StringBuffer都是继承自AbstractStringBuilder,底层都是利用可修改的char数组(JDK9之后是byte数组)

  • 在字符串不经常发生变化的业务场景优先使用String(代码清晰整洁)。如常量的声明,少量的字符串操作(拼接,删除等)。
  • 在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。尽量不使用String“+”,以免产生大量的中间对象,耗费空间且效率低下(新建对象,回收对象花费大量时间)(如JSON的封装)
  • 多线程情况下,如有大量的字符串操作情况,则应该使用StringBuffer。如HTTP参数解析和封装等。

String类源码分析

@intern
intern声明了字符串对象的规范形式,查找字符串是否存在于常量池中,
若不是则新加入进字符串池并且返回它的引用

public void intern(){
//2:string的intern使用
//s1是基本类型,比较值。s2是string实例,比较实例地址。
//字符串类型用equals方法比较时只会比较值
String s1 = "a";
String s2 = new String("a");
//调用intern时,如果s2的字符不在常量池,则加入常量池并返回常量的引用、
String s3 = s2.intern();
System.out.println(s1= =s2);
System.out.println(s1= =s3);
}

例:

String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = new String("ab");
String str6 = "a"+"b";
 
System.out.println(str5.equals(str3));
System.out.println(str5 == str3);
System.out.println(str5.intern() == str3);
System.out.println(str5.intern() == str4);
System.out.println(str5.intern()==str6);

结果:

true值相同
false new出来的不是字面量,5不和3共引用
true  5intern后接受返回的“ab”位置引用,和3共引用
false  字符串变量相加实际上是新建对象,和静态文本的相加不同
true   字面量相加的方式会进入常量池

总结下来就是new相加和带有一个变量的字符串相加不会加入字符串池

String类型的equals实现:

//字符串的equals方法 
 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; 
 }
 //从起点开始的内容比较

StringBuilder和StringBuffer

底层是继承父类的可变字符数组value

/** 
-The value is used for character storage. 
*/ 
char[] value; 
初始化容量为16 
/** 
-Constructs a string builder with no characters in it and an 
-initial capacity of 16 characters. 
 */
 public StringBuilder() {
  super(16); 
  }
这两个类的append方法都是来自父类AbstractStringBuilder的方法 
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; 
}
@Override 
public StringBuilder append(String str) { 
super.append(str); 
return this; 
} 
@Override 
public synchronized StringBuffer append(String str) { 
toStringCache = null; 
super.append(str); 
return this; 
}

append方法
StringBuffer在大部分涉及字符串修改的操作上加入了synchronized关键字来保证线程安全,效率较低。

String类型在使用+运算符,例如:
String a = “a”;
a=a+a;
这类情况时,实际上先把a封装成stringbuilder,调用append方法后再使用toString返回,所以当大量使用字符串加法时,会大量生产层stringbuilder实例,这是十分浪费的情形,
这种时候应当使用stringbuilder代替string进行操作

@扩容
#在append方法中调用了ensureCapacityInternal(count + len);
该方法是计算append后空间是否足够,不够的话需要进行扩容

public void ensureCapacity(int  minimumCapacity){
	if(minimumCapacity > 0)
		ensureCapacityIntery (minimumCapacity);
		}
private void ensureCapacityInternal(int minimumCapacity){
//overflow-conscious code
if (minimumCapacity - value.length > 0){
value = Arrays.copyOf(value,newCapacity(minimumCapacity));
}
}

如果新的字符串长度超过了value数组长度则进行扩容,扩容后的长度一般为原来的2倍加2,假如扩容后长度超过了jvm支持的最大数组长度MAX_ARRAY_SIZE。
如果新字符串长度超过int最大值,则抛出异常,否则直接使用数组最大长度作为新数组的长度

private int hugeCapacity(int minCapacity){
	if (Integer.MAX_VALUE - minCapacity < 0)
	throw new OutOfMemoryError();
	}
	return (minCapacity > MAX_ARRAY_SIZE)
	?minCapacity : MAX_ARRAY_SIZE;
	}

@删除

这两个类型的删除操作:
都是调用父类的delete方法进行删除

public AbstractStringBuilder  delete(int start ,int end){
	if(start < 0)
	throw new StringIndexOutOfBoundsException(start);
	if (end > count)
	end = count ;
	if(start > end)
	throw new StringIndexOutOfBoundsException();
	int len = end - start;
	if(len >0{
	System.arraycopy(value,start+len,value,start,count-end);
	count -=len;
}
return this;
}

事实上是将剩余的字符重新拷贝到字符数组value

这里用到system.arraycopy来拷贝数组,速度较快

@#system.arraycopy方法#
转自黄小斜转自知乎部分

在主流高性能的JVM上(HotSpot VM系、IBM J9 VM系、JRockit系等)
可以认为 System.arraycopy()在拷贝数组时是可靠高效的–如果发现不够可靠高效的
情况,反馈bug后很快会获得改进


java.lang.System.arraycopy()方法在java代码里声明为一个native方法。所以最native
的实现方式就是通过JNI调用JVM里的native代码来实现


String的不可变性 关于String的不可变性:


什么是不可变?
给一个已有字符串赋值,不是在原内存地址上改变,而是重新指向一个新对象,新地址

String和JVM的关系

下面我们了解下Java栈,Java堆,方法区和常量池

@java栈(线程私有数据区):

每个java虚拟机线程都有自己的java虚拟机栈,用来存放栈帧,每个方法执行的时候会同时创建一个栈帧(Stack Frame)用于储存局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直到完成的过程,就对应了一个栈帧在虚拟机栈中从入栈到出栈的过程。

@java堆(线程共享数据区):

在虚拟机启动是创建,此内存区域的唯一目的就是存放对象实例。几乎所有的对象实例都在这里分配

@方法区(线程共享数据区):

方法区在虚拟机启动的时候被创建,它存储了每个类的结构信息,例如运行时常量池、字段和方法数据、构造函数和方法的字节码内容、还包括在类、实例、接口初始化时用到的特殊方法。JDK8前永久代是方法区的一种实现,而JDK8中元空间替代了永久代,也可以说元空间是方法区的一种实现。

@常量池(线程共享数据区):

常量池被分范围两大类:静态常量池和运行时常量池。静态常量池也就是Class文件中的常量池,存在于Class文件中。运行时常量池(Runtime Constant Pool)是方法区的一部分,存放一些运行时常量数据

@字符串常量池

字符串常量池在运行时常量池之中(在JDK7之前存在运行时常量池中,在JDK7已经转移到堆中)。字符串常量池的存在使得JVM提高了性能和减少了内存开销。使用字符串常量池,每当我们使用字面量(String s = “1”;)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s
(引用s在java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s(引用s在java栈中)。

使用字符串常量池,每当我们使用关键字new(string s = new String"1";)
创建字符串常量时,JVM会首先检查字符串常量池,
如果该字符串已经存在于常量池中,那么就将此字符串对象的地址赋值给引用s
(引用s存在java栈中)。如果字符串不在常量池中,就会实例化该字符串并且放到
常量池中,并将此字符串对象的地址赋值给引用s(引用s在Java栈中)。

String为什么不可变

public final class String implementsjava.io.Serializable,
Comparable<String>,CharSequernce{
/*String本质上是个char数组,而且用final关键字修饰*/
private final char value[];
}

!分析
首先String类用final关键字修饰,这说明String不可继承。再看下面,String类的主力成员
字段value是一个char【】数组,而且用final修饰。

final修饰的字段创建以后就不可改变。但是虽然value本身是不可变,也只是数组的引用地址不可变。Array数组本身是可变的,它本身只是stack上的一个引用,数组的本体结构在heap堆中。
(定义为不可变的引用,但不影响可变的原位置)
String类里的value用final修饰,只是说stack里的这个叫value的引用地址不可变。没有说堆array里本身数据不可变。看下面这个例子,

final int [ ] value = {1,2,3};
int [ ] another = {4,5,6};
value = another ;
//编译器报错,final不可变 ,value用final修饰,编译器不允许将value指向堆区另一个地
//址。  但如果直接对数组元素动手,则可以进行操作
final int[ ] value = {1,2,3};
value[2]=100;
//这时候数组里已经是{1,2,100},所以String是不可变,
//关键是在后面的String方法中没有操作Array中的元素,没有暴露内部成员字段。
//private final char value [ ] 这一句里,private私有访问权限的作用都比final大。
//设计者还很小心的将整个String设置为禁止继承,所以String不可变的原因其实是
//底层的实现,而不是那个final。考验的是工程师构造数据类型,封装数据的实力

@不可变的好处?
为了安全。。。。
总结以下String的不可变性

  1. 首先final修饰的类只保证不能被继承,并且该类的对象在堆内存中的地址不会被改变。
  2. 但是持有String对象的引用本身是可以改变的,比如可以指向其他的对象
  3. final修饰的char数组保证了char数组的引用不可变。但是可以通过char[0] = ‘a’;
    修改值。不过String内部并不提供方法来完成这一操作,所以String的不可变也是基于代码封装和访问控制的。

(作者例:)

final class Fi {
	int a;
	final int b =  0;
	Integer s;
	}
	final char[ ]a = {'a'};
	final int[ ]b = {1};
	@Test
	public void final 修饰类(){
	//引用没有被final修饰,所以是可变的。
	//final只修饰了Fi类型,即Fi实例化的对象在堆中内存地址是不可变的。
	//虽然内存地址不可变,但是可以对内部的数据做改变。
	Fi f = new Fi();
	f.a = 1;
	System.out.println(f);
	f.a = 2;
	System.out.prinltn(f);
	//改变实例中的值并不改变内存地址
Fi ff = f;
//让引用指向新的Fi对象,原来的f对象由新的引用ff持有。
//引用的指向改变也不会改变原来对象的地址
f = new Fi();
System.out.println(f);
System.out.println(ff);
}

这里的对f.a的修改可以理解为char[0] = 'a’的操作。只改变数据值,不改变内存值。


拓展:
(再次提醒,本篇大部分来自b站up,公众号黄小斜(java技术江湖),使用请注意版权意识)
String工具类
基于apache-commons组件写的部分常用方法:

MAVEN依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lan3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>

代码:

public class StringUtils extends org.apache.commons.lang3.StringUtils { 
/** 值为"NULL"的字符串 */ 
private static final String NULL_STRING = "NULL"; 
private static final char SEPARATOR = '_'; 

/** 
*满足一下情况返回true<br/> 
*①.入参为空 
*②.入参为空字符串 
*③.入参为"null"字符串 
*@param string 需要判断的字符型 
* @return boolean 
*/ 
 public static boolean isNullOrEmptyOrNULLString(String string) { 
 return isBlank(string) || NULL_STRING.equalsIgnoreCase(string); 
 } 
 /*
 * 把字符串转为二进制码<br/> 
 * 本方法不会返回null 
 * @param str 需要转换的字符串 
 * @return 二进制字节码数组 
 */ 
public static byte[] toBytes(String str) { 
return isBlank(str) ? new byte[]{} : str.getBytes(); 
} 
/** 
* 把字符串转为二进制码<br/> 
* 本方法不会返回null  
* @param str 需要转换的字符串
* @param charset 编码类型
* @return 二进制字节码数组
* @throws UnsupportedEncodingException 字符串转换的时候编码不支持时出现 
*/ 
public static byte[] toBytes(String str, Charset charset) throws UnsupportedEncodingException { 
return isBlank(str) ? new byte[]{} : str.getBytes(charset.displayName()); 
} 
/** 
* 把字符串转为二进制码<br/> 
* 本方法不会返回null 
* @param str 需要转换的字符串 
* @param charset 编码类型 
* @param locale 编码类型对应的地区 
* @return 二进制字节码数组
* @throws UnsupportedEncodingException 字符串转换的时候编码不支持时出现 
* */ 
public static byte[] toBytes(String str, Charset charset, Locale locale) throws UnsupportedEncodingException { 
return isBlank(str) ? new byte[]{} : str.getBytes(charset.displayName(locale)); 
} 
/** 
* 二进制码转字符串<br/> 
* 本方法不会返回null  
* @param bytes 二进制码 
* @return 字符串 
* */ 
public static String bytesToString(byte[] bytes) { 
return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes); 
} 
/** 
*二进制码转字符串<br/> 
*本方法不会返回null 
*@param bytes 二进制码 
*@param charset 编码集 
*@return 字符串 
*@throws UnsupportedEncodingException 当前二进制码可能不支持传入的编码 
*/ public static String byteToString(byte[] bytes, Charset charset) throws UnsupportedEncodingException { 
return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes, charset.displayName()); 
} 
/** 
* 二进制码转字符串<br/> 
* 本方法不会返回null 
* @param bytes 二进制码 
* @param charset 编码集 
* @param locale 本地化 
* @return 字符串 
* @throws UnsupportedEncodingException 当前二进制码可能不支持传入的编码 
**/ 
public static String byteToString(byte[] bytes, Charset charset, Locale locale) throws UnsupportedEncodingException { 
return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes, charset.displayName(locale)); 
} 
/** 
* 把对象转为字符串 
* @param object 需要转化的字符串
* @return 字符串, 可能为空 
**/ 
public static String parseString(Object object) { 
if (object == null) {
return null;
} 
if (object instanceof byte[]) { 
return bytesToString((byte[]) object); 
} 
return object.toString(); 
} 
/** 
*把字符串转为int类型
*@param str 需要转化的字符串 
*@return int
*@throws NumberFormatException 字符串格式不正确时抛出 
**/ 
public static int parseInt(String str) throws NumberFormatException { 
return isBlank(str) ? 0 : Integer.parseInt(str); 
} 
/** 
*把字符串转为double类型 
*@param str 需要转化的字符串 
*@return double 
*@throws NumberFormatException 字符串格式不正确时抛出 
**/ 
public static double parseDouble(String str) throws NumberFormatException { 
return isBlank(str) ? 0D : Double.parseDouble(str); 
} 
/** 
* 把字符串转为long类型 
* @param str 需要转化的字符串 
* @return long 
* @throws NumberFormatException 字符串格式不正确时抛出
**/ 
public static long parseLong(String str) throws NumberFormatException { 
return isBlank(str) ? 0L : Long.parseLong(str); 
} 
/** 
* 把字符串转为float类型 
* @param str 需要转化的字符串 
* @return float 
* @throws NumberFormatException 字符串格式不正确时抛出
**/
public static float parseFloat(String str) throws NumberFormatException { 
return isBlank(str) ? 0L : Float.parseFloat(str); 
} 
/** 
* 获取i18n字符串 
* @param code
* @param args 
* @return 
*/ 
public static String getI18NMessage(String code, Object[] args) { 
//LocaleResolver localLocaleResolver = (LocaleResolver) SpringContextHolder.getBean(LocaleResolver.class); 
//HttpServletRequest request =((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); 
//Locale locale = localLocaleResolver.resolveLocale(request); 
//return SpringContextHolder.getApplicationContext().getMessage(code, args, locale); return ""; } 
/** 
* 获得用户远程地址 
* @param request 请求头 
* @return 用户ip 
* */  
public static String getRemoteAddr(HttpServletRequest request) { 
String remoteAddr = request.getHeader("X-Real-IP"); 
if (isNotBlank(remoteAddr)) { 
remoteAddr = request.getHeader("X-Forwarded-For"); 
} else if (isNotBlank(remoteAddr)) { 
remoteAddr = request.getHeader("Proxy-Client-IP"); 
} else if (isNotBlank(remoteAddr)) { 
remoteAddr = request.getHeader("WL-Proxy-Client-IP"); 
} 
return remoteAddr != null ? remoteAddr : request.getRemoteAddr(); 
} 
/** 
* 驼峰命名法工具 
* @return toCamelCase(" hello_world ") == "helloWorld" 
* toCapitalizeCamelCase("hello_world") == "HelloWorld" 
* toUnderScoreCase("helloWorld") = "hello_world" 
* */ 
public static String toCamelCase(String s, Locale locale, char split) { 
if (isBlank(s)) { return ""; } s = s.toLowerCase(locale); 
StringBuilder sb = new StringBuilder(); 
for (char c : s.toCharArray()) { 
sb.append(c == split ? Character.toUpperCase(c) : c); 
} 
return sb.toString(); 
} public static String toCamelCase(String s) { 
return toCamelCase(s, Locale.getDefault(), SEPARATOR); } 
public static String toCamelCase(String s, Locale locale) { 
return toCamelCase(s, locale, SEPARATOR); 
} 
public static String toCamelCase(String s, char split) { 
return toCamelCase(s, Locale.getDefault(), split); 
} 
public static String toUnderScoreCase(String s, char split) { 
if (isBlank(s)) { return ""; 
} 
StringBuilder sb = new StringBuilder(); 
for (int i = 0; i < s.length(); i++) { 
char c = s.charAt(i); boolean nextUpperCase = (i < (s.length() - 1)) && Character.isUpperCase(s.charAt(i + 1)); 
boolean upperCase = (i > 0) && Character.isUpperCase(c); 
sb.append((!upperCase || !nextUpperCase) ? split : "").append(Character.toLowerCase(c)); 
} 
return sb.toString(); 
} 
public static String toUnderScoreCase(String s) 
{ return toUnderScoreCase(s, SEPARATOR); 
} 
/** 
* 把字符串转换为JS获取对象值的三目运算表达式 
* @param objectString 对象串 
* 例如:入参:row.user.id/返回:!row?'':!row.user?'':!row.user.id?'':row.user.id 
* */ 
public static String toJsGetValueExpression(String objectString) {
StringBuilder result = new StringBuilder(); 
StringBuilder val = new StringBuilder(); 
String[] fileds = split(objectString, "."); 
for (int i = 0; i < fileds.length; i++) { 
val.append("." + fileds[i]); 
result.append("!" + (val.substring(1)) + "?'':"); 
} 
result.append(val.substring(1)); return result.toString(); 
} 
}

如上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值