所有字符串用“”进行定义,也可以利用“+”实现字符串的连接
字符串不是基本数据类型。
范例:String 类的实例化
String str = "www.mldn.cn";
System.out.println(str);
String类中可以保存字符串的主要原因是由于其中定义了一个数组,也就是在String里所有的字符串中每一个字符的数据都是保存在数组中。
JDK1.8前String保存的是字符数组,
private final char[] value;
而JDK1.9后String保存的是字节数组。
private final byte[] value;
原来所谓的字符串是对数组的包装利用,既然包装的是数组,所以字符串中的内容是不能改变的。
还需注意,String类中除了可以直接赋值为对象进行实例化,也可以用传统的构造方法进行对象的实例化处理:public String(String str);
范例:利用构造方法进行实例化
package 面向对象;
public class StringDemo00 {
public static void main(String[] args) {
String str = "www.mldn.cn";
System.out.println(str);
String str1 = new String(str);
System.out.println(str1);
}
}
字符串的比较:
可以使用String类提供的equals方法进行比较:
public boolean equals(String str);
范例:
利用equals()实现字符串的比较
String比较重"=="与equals()的区别?
“==”进行的是数值比较,如果用于对象比较,则比较的是两个内存的地址数值;
equals():是类提供的一个比较方法,可以直接进行字符串内容的判断。
package 面向对象;
public class StringDemo00 {
public static void main(String[] args) {
String str = "www.mldn.cn";
System.out.println("字符串的直接赋值结果为:");
System.out.println(str);
String str1 = new String(str);
System.out.println("\n用原始构造方法给字符串赋值时的结果为:");
System.out.println(str1);
int x = 10;
int y = 10;
System.out.println("\n数值的直接比较时:");
System.out.println(x == y);
String strA = "mldn";
String strB = new String("mldn");
System.out.println("\n直接比较两个对象");
System.out.println(strA == strB); //不同的对象地址比较
System.out.println("\n使用String类中的equals()方法进行比较时");
System.out.println(strA.equals(strB)); //比较对象的内容,此时相等
}
}
输出结果:
字符串的直接赋值结果为:
www.mldn.cn
用原始构造方法给字符串赋值时的结果为:
www.mldn.cn
数值的直接比较时:
true
直接比较两个对象
false
使用String类中的equals()方法进行比较时
true
字符串常量
字符串常量是String类的匿名对象:
程序中所有的小数默认都是double类型,但是对于字符串而言,首先程序中不会提供有字符串这样的基本类型,只有String类,故任何使用""定义的字符串常量实际上描述的都是一个String类的匿名对象。
String str = "mldn"

所谓的String类的直接赋值直接描述的是,将一个匿名对象设置一个具体的名字。
范例:观察匿名对象的存在
匿名对象是一个类的实例化对象,则该匿名对象可以调用该类的方法
String str = "mldn";
System.out.println("mldn".equals(str)); //true
此时发现字符串常量可以明确的调用equals()方法实现对象相等的判断,所以可以得出结论:程序中的确没有字符串常量,只有String类的匿名对象。
关于对象相等判断的小技巧:
- 在项目开发是,如果某些数据是用户输入,并且要求这些数据为一个指定内容的情况下, 建议将字符串常量写在前面。
equals()方法中提供有一个可以回避null的判断,所以如果将字符串常量写在前面,则调用equals()方法时永远都不可能出现"NullPointerException";因为字符串是一个匿名对象,匿名对象一定是开辟好堆内存空间的对象。
package 面向对象;
/**
* 关于对象相等判断的小技巧:
*
* 在项目开发是,如果某些数据是用户输入,并且要求这些数据为一个指定内容的情况下,
* 建议将字符串常量写在前面。
*/
public class StringDemo01 {
public static void main(String[] args) {
String input = "mldn"; //用户输入的内容
System.out.println(input.equals("mldn")); //true
String str = null;
//Exception in thread "main" java.lang.NullPointerException
// System.out.println(str.equals("mldn")); //出现空指针异常
/*
equals()方法中提供有一个可以回避null的判断
所以如果将字符串常量写在前面,
则调用equals()方法时永远都不可能出现"NullPointerException";
*/
System.out.println("mldn".equals(str));
}
}
输出结果:
true
false
String类两种实例化对象方法的比较
1、分析直接赋值的对象实例化模式
在程序中只需要将一个字符串赋值给String类对象就可以实现对象的实例化处理。
利用直接赋值实例化String的形式还可以实现同一个字符串对象睡的共享操作。
范例:观察String直接赋值时的数据共享
String strA = "mldn";
String strB = "mldn";
System.out.println(strA == strB); //对象的地址判断 true
结果返回为true,得出结论,这两个对象所指向的堆内存是同一个。
如图:

Java程序里面提供有一个专门的字符串池(字符串数组)。
String类池:

范例:分析字符串池
String strA = "mldn";
String strB = "mldnJava";
String strC = "mldn";
System.out.println(strA == strC); //对象的地址判断 true

2、分析构造方法实例化
String类为了满足于设计的结构要求,也提供有构造方法实例化的做法。
String strA = new String("mldn");
对于本程序而言,可以通过内存关系图进行查看:

此时会开辟两块堆内存空间,而后只会使用一块,而另外一块由于字符串常量所定义的匿名对象将成为垃圾空间。
直接赋值与构造方法实例化的区别:

此时会开辟两块堆内存空间,而后只会使用一块,而另外一块由于字符串常量所定义的匿名对象将成为垃圾空间。
除了以上的特点之外,在使用构造方法实例化string对象时不会出现自动保存到字符串池的处理。
范例:观察构造防范实例化对象时的操作
package 面向对象;
public class StringDemo02 {
public static void main(String[] args) {
String strA = "mldn";
String strB = new String("mldn");
System.out.println(strA == strB); //对象的地址判断 false
}
}
可以发现构造方法实例化的对象实际上是一种自己专用的内存空间,但是在String类中也提供有帮助开发者实现手工入池的处理情况,该方法时:public String intern();
范例:观察手工入池
package 面向对象;
public class StringDemo02 {
public static void main(String[] args) {
String strA = "mldn";
//字符串手工入池
String strB = new String("mldn").intern();
System.out.println(strA == strB); //对象的地址判断 true
}
}
在使用构造方法定义对象后,由于使用了intern()方法,即便是构造出来的String对象内容也可以手工进入字符串池,但是该方法太啰嗦了。
面试题:请解释下String类两种实例化方式的区别?
1、直接赋值:只会产生一个实例化对象,并且可以自动保存到对象池中,以实现该字符串实例的重用。
2、构造方法:会产生两个实例化对象,并且不会自动入池,无法实现对象重用,可以利用intern()方法实现手工入池。
String对象(常量)池:
对象池的主要目的是实现数据的共享处理。以String对象池为例,里面的内容主要是为了重用,而重用实际上就是共享设计。
Java中对象池可以分为两种:
1、静态常量池
程序在加载时会自动将此程序中保存的字符串、普通的常量、类和方法的信息等全部进行分配;
2、运行时常量池
当一个程序加载后,里面可能有一些变量,这个时候提供的常量池
范例:静态常量池
package 面向对象;
public class StringDemo02 {
public static void main(String[] args) {
String strA = "www.mldn.cn";
String strB = "www" + ".mldn" + ".cn";
System.out.println(strA == strB); //对象的地址判断 true
}
}
输出结果为true
分析:本程序中所给出的内容都是静态常量数据(字符串的常量都是匿名对象),所以在程序加载时会自动帮助开发者处理好相应的连接。

所以在实际开发中可能会遇到这样的情况,尽量输入的内容和实际内容一样,但是判断结果仍然不相等。那就要用equals()方法比较两个对象的内容。因为用==,会出现静态常量池和运行时常量池两种情况。
字符串内容不可修改
在String类中包含的是一个数组,当设置了一个字符串后,会自动进行数组空间的开辟,那么开辟的内容,长度是固定的。
String内容的保存:JDK1.9后String是以byte数组形式保存

范例:
package 面向对象;
public class StringDemo02 {
public static void main(String[] args) {
String str = "www.";
str += "mldn.";
str = str + "cn";
System.out.println(str);
}
}
输出:www.mldn.cn
分析:

通过此时程序发现,在整个处理过程中,字符串常量的内容并没有发生任何改变,改变的只是一个string类对象的引用,并且这种改变有可能带来大量的垃圾空间。
范例:
package 面向对象;
public class StringDemo02 {
public static void main(String[] args) {
String str = "www.";
for (int i = 0; i < 10; i++) {
str += i;
}
System.out.println(str);
}
}
输出结果:
如果本程序这样写,将会产生很多垃圾空间,并且String对象的指向要修改很多次,这种程序的性能非常差,因此String类在以后的开发中不要进行内容的频繁修改。
Java中的主方法:
主方法组成:
public static void main(String[] arg)
public:描述的一种访问权限,主方法是一切的开始点,开始点一定是公共的;
static:程序的执行是通过类名称完成的,所以表示此方法是由类直接调用;
void:主方法是一切的起点,起点一旦开始就不能回去
main:是一个系统定义好的方法名称
String[] args:字符串的数组,可以实现程序启动参数的接收;
范例:输出启动参数
范例:输出启动参数
package 面向对象;
public class StringDemo02 {
public static void main(String[] args) {
for(String arg: args) {
System.out.print(arg + ",");
}
}
}
在用命令实现程序执行的时候可以设置参数,每一个参数之间使用空格分割
如:Java StringDemo02 first second
但是千万要记住几点:如果参数本身拥有空,则必须用""进行包装。
如;Java StringDemo02 "hello world" "hello mldn"
以后可以暂时通过这种暂时启动参数实现数据的输入的模拟。