字符串
String表示字符串类型,String虽然本身不属于基本数据类型,但是可以像基本数据类型一样使用方便,是JVM对String类做了特殊的处理
String类只所以特殊,其原因主要就是有两种不同的对象实例化方式
方式一:采用直接赋值的形式为String类进行赋值
方式二: 通过String类的构造方法完成对象实例化
范例: 通过直接赋值的方式为String类实例化对象
public static void main(String[]args){
String str="hello world"; //定义一个字符串 并且直接赋值
System.out.println(str);
}
String明确告诉大家肯定是一个类,程序中出现的str就是String类的对象名称,String类的赋值操作就是为str这个对象进行实例化操作
范例; 通过String类的构造方法进行对象的实例化操作
public static void main(String[]args){
String str=new String("hello world");
System.out.println(str);
}
通过以上的代码比较,两种方式都可以为String类进行对象的实例化操作,这两种方式有什么不同,使用哪个比较好?
字符串的比较
如果两个int类型进行比较的时候,直接使用==就可以了,现在使用==进行字符串的比较
public static void main(String[]args){
String str1="hello";
String str2=new String("hello");
String str3=str2;//引用传递
System.out.println(str1==str2); //false
System.out.println(str1==str3); //false
System.out.println(str2==str3); //true
}
虽然以上的字符串的内容完全一样,但是使用了==进行比较的时候结果就不一样了,为什么不一样的结果,通过内存分析图来解答
通过上图的分析可以发现,使用==运算符比较的是内存的地址,对于String这个类来说使用==比较的是对象的内存地址,并没有比较字符串的内容,如果想要比较字符串的内容则必须使用String类提供的操作方法 equals()
public boolean equals(Object anObject); 字符串内容的比较
范例:使用equals 进行方法的比较
public static void main(String[]args){
String str1="hello";
String str2=new String("hello");
String str3=str2;//引用传递
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3)); //true
System.out.println(str2.equals(str3)); //true
}
通过以上的代码的分析,可以得出如下结论:
String进行字符串比较的时候 == 与 equals的区别是什么?
== : 比较的是内存地址,属于数值比较
equals :比较的是字符串的内容,属于内容比较
字符串常量是String类的匿名对象
java中使用” ” 来定义一个字符串,在这里面每一个字符串都表示一个String类的对象,即每一个字符串常量就是一个String类的匿名对象
范例:验证字符串是对象的概念
public static void main(String[]args){
String str="hello";
System.out.println("hello".equals(str));
}
从以上的程序上验证了一个字符串就是一个String类的匿名对象,通过匿名对象可以调用类中的属性与方法. 由此引出在String处理字符串比较的时候常用的用法如下
范例: 字符串比较,模拟用户输入
public static void main(String[]args){
String str=null;
if("hello".equals(str)){
System.out.println("条件满足");
}
}
通过以上的程序,假设在处理字符串的时候,字符串是用户自己输入的时候,使用匿名对象进行字符串的比较可以避空指向异常的出现
String类的两种实例化方式的区别?
清楚了String的操作之后,现在就要分析一个问题,两种实例化方式的区别 ,在开发中使用哪个更好
范例:分析直接赋值的情况
public static void main(String[]args){
String str1="hello"; //直接赋值
String str2="hello"; //直接赋值
String str3="hello"; //直接赋值
System.out.println(str1==str2);
System.out.println(str2==str3);
System.out.println(str1==str3);
}
以上的程序使用了直接赋值的方式。实例化了3个对象,本程序使用了==进行比较 发现3个字符串的内存地址完全相同,也就是3个对象实际上开辟了一块空间
对于以上的案例,直接赋值字符串都相同的原因如下:
在String类进行设计的时候,采用了一种叫做共享设计模式的概念,每一个运行的JVM底层会有一个字符串的对象池(Object Pool),如果用户采用直接赋值的时候,会将字符串的内容放到对象池中,以供其他String直接赋值方式的对象使用,如果新声明的字符串不在池中,则会新开辟一块空间,继续放到池中供下次使用
范例:分析构造方法实例化的情况
public static void main(String[]args){
String str=new String("hello");
}
通过以上的画图分析,由于每一个字符串都是一个String类的匿名对象,首先会在堆内存中开辟一块空间保存字符串常量 hello ,而后又使用了关键字new 又开辟了另外一块堆内存空间,实际上使用的通过new关键字开辟的空间,匿名字符串对象则会变为垃圾空间存在等待被回收
另外使用构造方法实例化的时候,String类的对象不会保存在字符串常量池中,但是课题通过手动调用intern()方法手动入池
范例:验证入池的问题
public static void main(String[]args){
String str1=new String("hello"); //不会进入常量池
String str2="hello";
String str3="hello";
System.out.println(str1==str2); //false
System.out.println(str1==str3); //false
System.out.println(str2==str3); //true
}
以上的程序可以发现,通过构造方法实例化String类对象的时候是不会自动进入到对象常量池的,但是可以通过手动入池的方法完成
public static void main(String[]args){
String str1=new String("hello").intern(); //不会进入常量池
String str2="hello";
String str3="hello";
System.out.println(str1==str2); //false
System.out.println(str1==str3); //false
System.out.println(str2==str3); //true
}
通过以上的分析,得出如下结论:
String类的两种实例化方式的区别?
直接赋值: 只开辟一块堆内存空间,并且字符串的内容可以自动入池
构造方法:开辟两块堆内存空间,其中一块将称为垃圾空间,并且不能自动的入池,需要手动调用intern()方法进行入池
3.5 字符串的内容一旦声明则不可改变
在使用String类的时候有一个重要的特点,那就是字符串的内容一旦声明则不可改变
范例: 字符串内容声明后不可改变
public static void main(String[]args){
String str="hello";
str+="world";
System.out.println(str);
}
通过以上的程序,进行字符串的拼接操作,str这个对象经过了多次的字符串拼接内容已经进行了改变,实际上来说,改变的是对象的指向而不是堆内存中的内容.
通过上图可以发现,每一次进行字符串拼接的时候都会产生垃圾空间,并且内容没有改变,改变的只是字符串的内存地址指向,如果对字符串进行大量的拼接操作则会产生大量的垃圾
范例: 应该避免出现的代码
public static void main(String[]args){
String str="";
for(int i=0;i<100000;i++){
str+=i;
}
System.out.println(str);
}
范例:通过方法修改字符串的内容进行验证
public static void main(String[]args){
String str="hello";
fun(str);
System.out.println(str);
}
public static void fun(String str){
str+="world";
}
通过以上的代码,进行验证说明在方法中进行字符串的拼接之后,在方法执行完毕之后,字符串的内容没有被修改,说明字符串的内容是不可被改变的
String类的常用方法
String类除了之前讲解的,intern()方法以及equals()方法之外还存在大量操作方法,这些方法可以通过java api进行查看
通过javaapi 观察String类可以得出如下分类:
构造方法
1) public String(byte[] bytes,int offset,int length)把一个字节数组转换为一个字符串
Offset 为数组的开始位置,length 长度
普通方法
- public char charAt(int index) 根据索引找到指定的字符
- public String concat(String str) 字符串的拼接方法
- public boolean equalsIgnoreCase(String anotherString) 字符串内容的比较忽略大小写
- public int indexOf(String str)
- public String substring(int beginIndex) 字符串的截取
- public String substring(int beginIndex,int endIndex) 字符串的截取
- public char[] toCharArray() 把一个字符串转换为字符数组
- public byte[] getBytes() 把字符串变为字节数组
- public boolean startsWith(String prefix)判断是否以指定的字符开头
- public boolean endsWith(String suffix) 判断是否以指定字符结尾
- public boolean contains(CharSequence s) 判断一个字符串子串是否存在
- public String replaceAll(String regex,String replacement) 字符串替换 全部替换
- public String replaceFirst(String regex,String replacement)替换首个
- public String[] split(String regex)按照指定的字符串进行拆分全部拆分
- public String[] split(String regex,int limit) 拆分为指定的长度
- public boolean isEmpty()判断字符串是否为空
- public String trim()去掉字符串的左右空格
- public String toUpperCase()把字符串全部转换为大写
- public String toLowerCase() 把字符串全部转换为小写
范例
范例:验证charat方法
public static void main(String[]args){
String str="hellworld"; //定义一个字符串
char c=str.charAt(0); //取第7个索引上的字符
System.out.println(c);
}
范例2:进行字符串与字符数组的转换,并且把一个小写字符串变为大写字符串,最终要把字符数组在转换为字符串
public static void main(String[]args){
String str="hellworld"; //定义一个字符串
char[] array=str.toCharArray(); //把字符串转换字符数组
for(int i=0;i<array.length;i++){
array[i]-=32; //变大写
}
System.out.println(new String(array)); //把字符数组转换为字符串
}
范例3:判断一个字符串是否由数字组成
public static void main(String[]args){
String str="123";
System.out.println(isNumber(str));
}
public static boolean isNumber(String temp){
char[] data=temp.toCharArray();
for(int i=0;i<data.length;i++){
if(data[i]<'0'||data[i]>'9'){
return false;
}
}
return true;
}
字符串除了与字符进行相互转换之外,还可以与字节进行转换
范例4:完成小写字母变大写字母的操作
public static void main(String[]args){
String str="helloworld";
byte[]data=str.getBytes(); //把字符串变为字节数组
for(int i=0;i<data.length;i++){
System.out.print(data[i]+",");
data[i]-=32;
}
System.out.println(new String(data));
}
范例5:判断字符串的开头与结尾
public static void main(String[]args){
String str="**@@hello##";
System.out.println(str.startsWith("**")); //判断是否以**开头
System.out.println(str.endsWith("o"));
}
范例6:查找字符串是否存在
public static void main(String[]args){
String str="helloworld";
System.out.println(str.contains("hello")); //查找字符串
System.out.println(str.contains("xx"));
}
范例7: 从指定的位置开始查找字符的位置 indexOf
public static void main(String[]args){
String str="binbin.com";
if(str.indexOf("@")!=-1){
System.out.println("符合验证");
}
}
范例8:字符串的替换操作
public static void main(String[]args){
String str="hello world";
System.out.println(str.replaceAll("l","_")); //替换全部
System.out.println(str.replaceFirst("l","_")); //替换首个
}
范例9:字符串的截取
public static void main(String[]args){
String str="helloworld";
System.out.println(str.substring(6)); //从指定位置截取
System.out.println(str.substring(0,5)); //从指定位置到结束位置的截取
}
范例:字符串的拆分
public static void main(String[]args){
String str="hello world!!";
String[]result=str.split("l"); //按照空格来拆分;
for(int i=0;i<result.length;i++){
System.out.print(result[i]+",");
}
}
范例10:拆分为指定的个数
public static void main(String[]args){
String str="hello world!!";
String[]result=str.split("",2); //按照指定的长度拆分
for(int i=0;i<result.length;i++){
System.out.print(result[i]+",");
}
}
范例11:判断字符串是否为空
public static void main(String[]args){
String str="hello";
System.out.println(str.isEmpty()); //false
System.out.println("".isEmpty()); //true;
System.out.println(str.length()); //得出字符串的长度
}
范例:去掉字符串左右空格
public static void main(String[]args){
String str=" hello ";
System.out.println(str);
System.out.println(str.trim()); //去掉左右空格
}