String类的基本特点

版权声明:本文为博主原创文章,转载请注明。 https://blog.csdn.net/mrbacker/article/details/82564099

几乎所有的项目开发过程之中都一定会存在String类的使用,但是String本身的定义有一些差别,以及在使用上是有一些注意事项的。

String类两种实例化方式

String可以直接采用赋值的形式进行处理,这一点感觉上和基本数据类型是非常相似的。

范例: 直接复制实例化对象

public class StringDemo {
	public static void main(String args[]) {
		//str 是一个对象,那么"hello"就应该保存在堆内存之中
		String str = "hello";
		System.out.println(str);
	}
}

但是String本身毕竟是一个类。那么既然是类,类中就一定会提供有构造方法,而在String类里面恰好提供了以下的一个构造方法。

构造: public String(String str)

public class StringDemo {
	public static void main(String args[]) {
		// 该过程符合传统做法,有类之后通过关键字new进行对象实例化
		String str = new String("hello");
		System.out.println(str);
	}
}

暂时不需要考虑这两者的区别以及使用,关键是要清楚String类现在提供有两种实例化对象的模式。

字符串相等比较

如果说现在有两个int型的变量判断其相等可以使用"=="完成。

范例: 观察基本数据类型比较

public class StringDemo {
	public static void main(String args[]) {
		int x = 10;
		int y = 10;
		System.out.println(x == y);
	}
}

那么如果说现在在String类的对象使用了"=="呢?

范例: 观察String直接使用"=="比较

public class StringDemo {
	public static void main(String args[]) {
		String str1 = "hello";
		String str2 = new String("hello");
		System.out.println(str1 == str2);
	}
}

现在两个字符串的内容是相同的,而使用"=="比较之后内容是不同的,于是要想得出结论,需要进行内存分析。

字符串常量是String类的匿名对象

在任何语言的底层上面都不可能会提供有直接字符串类型,现在所谓的字符串只是高级语言提供给用户方便开发的支持而已。所以在Java里面本身也没有提供有字符串常量的概念,所以使用“""”定义的内容本质上来讲都是String的匿名对象。

范例: 观察字符串操作

public class StringDemo {
	public static void main(String args[]) {
		String str = "hello";
		System.out.println(str.equals("hello"));
		System.out.println("hello".equals(str));
	}
}

那么在之前出现的String str = "hello";,本质上就是将一个匿名的String类对象设置有名字,而且匿名对象一定保存在堆内存之中。

提醒: 在日后的的开发的过程之中,如果要判断用户输入的字符串是否等同与指定的字符串,那么一定要将字符串写在前面。

  • 比较的操作方法一:
public class StringDemo {
	public static void main(String args[]) {
		String input = null;
		System.out.println(input.equals("hello"));
	}
}

在进行输入数据接受的时候必须考虑用户没有输入数据的问题,如果以上面的代码为例,用户真没有输入,则执行的时候一定会出现NullPointerException问题。

  • 任何的字符串常量都是String匿名对象,所以该对象永远不可能为null
public class StringDemo {
	public static void main(String args[]) {
		String input = null;
		System.out.println("hello".equals(input));
	}
}

那么在以后进行比较的时候强烈建议如上的写法,把字符串写在前面。

String类两种对象实例化的区别

现在已经给出了String类的两种实例化操作模式,那么在实际的开发之中使用哪一种会更好,以及彼此之间的区别有哪些呢?

  1. 采用直接赋值
public class StringDemo {
	public static void main(String args[]) {
		String str = "hello";
	}
}

那么下面按照同样的模式继续进行新的字符串对象创建。

public class StringDemo {
	public static void main(String args[]) {
		String str = "hello";
		String str2 = "hello";
		String str3 = "hello";
		System.out.println(str == str2); //true
		System.out.println(str == str3); //true
		System.out.println(str2 == str3); //true
	}
}

以上是通过程序的运行结果来分析出内存关系,但是从另外一个方面来讲为什么现在并没有开辟新的堆内存空间。

String类的设计使用了一个共享设计模式。

在JVM底层实际上会自动维护一个对象池(字符串对象池),如果采用了直接复制的模式进行String类实例化操作,那么该实例化对象(字符串)将自动保存到这个对象池之中,如果下次继续有人使用了直接复制的模式声明了String类的对象,那么如果此时对象池之中有指定的内容,那么将直接进行引用,如果没有,则开辟新的字符串对象,而后将其保存在对象池之中以供下次使用。(所谓的对象池就是一个对象数组)。

范例: 观察与常量的比较

public class StringDemo {
	public static void main(String args[]) {
		String str = "hello";
		System.out.println(str == "hello"); //  true
		System.out.println("hello" == "hello"); // true
	}
}

此时返回的全部都是true,这一方面是与共享设计有关,另一方面就是JDK的版本不同可能也会存在有若干差异。

  1. 采用构造方法
    类对象使用构造方法进行实例化才属于标准做法,那么来分析如下的一段程序:
    String str = new String("hello");
    那么通过分析可以确认一点的是,如果使用了构造方法将会开辟两块堆内存空间,并且其中有一块堆内存空间将成为垃圾空间,除了这一缺点之外,实际会也会对字符串共享产生问题。

范例: 观察字符串共享问题

public class StringDemo {
	public static void main(String args[]) {
		//该字符串常量并没有保存在对象池之中
		String str = new String("hello");
		String str2 = "hello";
		System.out.println(str == str2); //false
	}
}

那么这个时候并不表示不能够进入到对象池保存,关键的问题是:需要手工来处理,在String类里面有一个方法可以自动实现入池的操作:public String intern();

public class StringDemo {
	public static void main(String args[]) {
		//该字符串常量并没有保存在对象池之中
		String str = new String("hello").intern();
		String str2 = "hello";
		System.out.println(str == str2); //true
	}
}

面试题:请解释String类中两种实例化的区别:

  • 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以保存在对象池中以供下次使用。
  • 构造方法:会开辟两块堆内存空间,一块将成为会成为垃圾,并且不会自动保存在对象池中,可以使用intern()方法手工入池。

字符串常量不可变更

所有的语言对于字符串的底层实现都是字符数组,数组的最大缺陷就是长度固定,所以在定义字符串常量的时候,本身它的内容是不可改变的。

范例: 观察以下代码

public class StringDemo {
	public static void main(String args[]) {
		String str = "hello";
		str = str + " world";
		str += "!!!";
		System.out.println(str); // hello world!!!
	}
}

那么以上的字符串的变更是字符串对象的变更,而并不是字符串的内容,以上的代码执行操作如下:字符串修改

可以发现字符串上没有发生任何的变化,但是字符串对象的引用却一直在改变,而且会形成大量的垃圾,正是因为String的这个特点,所以如下的代码不应该在开发之中出现。

public class StringDemo {
	public static void main(String args[]) {
		String str = "hello";
		for (int i = 0; i < 1000;  i++) {
			str += i;
		}
		System.out.println(str);
	}
}

如果说现在有很多的用户都使用同样的操作,那么产生的垃圾数量就相当客观了。

总结

  1. 字符串的使用就采用直接赋值的模式完成。
  2. 字符串的比较就是用equals()方法实现。
  3. 字符串没事别改变太多。

猜你喜欢

转载自blog.csdn.net/mrbacker/article/details/82564099