一、初阶前言 javaSe
二、初阶认识javaSE,数据类型与运算符
三、java初识:类和对象
四、javaSE 【面向对象编程】初阶 多态、抽象类、接口
有三种方式:
//第一种:
String str="hello";
System.out.println(str);
//第二种:
String str1=new String("baba");
System.out.println(str1);
前面两种都是引用类型,那为什么没有toString方法尼?
引用类型重写toString方法:toString() 方法可把一个 Number 对象转换为一个字符串,并返回结果。
在String底层就有toString方法
返回是this,this所指向了System.out.println,在println底层是已经写好了怎么该怎么打印对象类里面的内容。
//第三种:
char[] ch={
'a','b','c'};
String str3=new String(ch);
System.out.println(str3);
第三种方法的底层实现如图所示,不在赘述。
直接上代码
public static void main(String[] args) {
String str="hello";
String str2="hello";
System.out.println(str==str2);
}
大家看看运行结果会是什么尼?————————没错就是true!!!,就是这么简单。
那我们再看下一个代码:
public static void main(String[] args) {
String str=new String("hello");
String str2=new String("hello");
System.out.println(str==str2);
}
大家觉得这个运行结果是什么尼??——————没错运行出来就是false,就是这么的坑,看图解释吧。
new了str和str2,就是在堆上开辟了两个空间来存储hello,也就是内存中存在两份hello,
String使用==比较并不是在比较字符串里的内容,而是比较两个引用是否指向同一个对象。
Java 中要想比较字符串的内容, 必须采用String类提供的equals方法
public static void main(String[] args) {
String str=new String("hello");
String str2=new String("hello");
System.out.println(str.equals(str2));
}
这样运行的结果就是true了。
是用来比较两个字符串的内容是否相等
但他也有注意事项:
有两种比较方式;
//方式一
String str=new String("hello");
System.out.println("hello".equals(str));
//方式二
System.out.println(str.equals("hello"));
两种方法都可以比较,但是那种最优尼??
跟推荐方式一,一点str为null,方式二就会报出NullPointerException,所以方式一更安全。
上代码举例
public static void main(String[] args) {
String str="hello";
String str2=new String("hello");
System.out.println(str==str2);
}
大家看看这段代码,就不猜了,运行结果是false,上图理解
在上面说过String==比较的是两个引用是否指向同一个对象,图上很明显指向的都是,不同的对象,所以是fales,同时也引出了字符串常量池概念。
字符串常量池是存放在堆里的一块内存,里面存的是String类的对象实例化操作 用双引号里面的内容,如果里面放入多个同样的内容,则舍去多余的,只留下一个,这样也节约了内存空间。
为加深印象再多看看代码:
public static void main(String[] args) {
String str="co2";
String str2="co2o2";
String str3=str+"o2";
System.out.println(str2==str3);
}
看图可知所以是false。
public static void main(String[] args) {
String str="co2o2";
String str2=new String("co2")+new String("o2");
System.out.println(str==str2);
}
运行结果:false
intern()方法的作用
一、如果SCP(字符串常量池)中存在A内容一样的字符串对象C,就返回C
二、否则,将A 加入到SCP中,返回A
上代码:
public static void main(String[] args) {
String str="co2o2";
String str2=new String("co2o2");
str.intern();
System.out.println(str==str2);
}
intern()方法常量池中有相同的内容,则返回存在的内容,但是str还是指向的另一个对象,所以为false。
再来看一个复杂一点的代码:
public static void main(String[] args) {
String str=new String("2")+new String("2");
str.intern();
String str2="22";
System.out.println(str==str2);
}
看图可知最后指的都是同一个对象,所以运行结果是true.
public static void main(String[] args) {
String str="hello";
String str2=str;
str="world";
System.out.println(str==str2);
}
再来看看复杂点的代码:
public static void func(String str) {
str="haha";
}
public static void main(String[] args) {
String str="hello";
func(str);
System.out.println(str);
}
上图是传引用只是改变方向,改变内容是通过这个引用修改他指向的这个对象。
再看一个代码:
public class Test {
String str = new String("good");
char[ ] ch = {
'a' , 'b' , 'c' };
public static void main(String args[]){
Test ex = new Test();
ex.change(ex.str,ex.ch);
System.out.print(ex.str + " and ");
System.out.print(ex.ch);
}
public void change(String str,char ch[ ]){
str = "test ok";
ch[0] = 'g';
}
}
这段代码和图像更好的解释了引用可以改变方向,但是不改变里面的内容,里面的str只是改变了引用就没有改变里面的内容,数组ch通过了这个引用修改他指向的这个对象。
回顾引用:
引用相当于一个指针, 里面存的内容是一个地址. 我们要区分清楚当前修改到底是修改了地址对应内存的内容发生改变了, 还是引用中存的地址改变了
还是先看代码
public static void main(String[] args) {
String str = "hello" ;//不可变
str = str + " world" ;
str += " !!!" ;
System.out.println(str);
}
通过代码和图,可以看出String的对象是不可变的,本身不会发生变化,str却引用了多个其他对象,他不会直接在hello后面直接+world !!!,而是创建多个对象,最后多个对象拼接而成为hello world !!!。
尽然StringBuffer和StringBuilder前面都有String,那么肯定跟String脱不了关系,先来说说他们的区别,看代码:
public static void main(String[] args) {
String str="hello";
StringBuffer str2="haha";//这么写编译器会报错
StringBuilder str3="lala";//这么写编译器会报错
}
public static void main(String[] args) {
StringBuffer str2=new StringBuffer("haha");
StringBuilder str3=new StringBuilder("lala");
//这么写编译器才能通过
}
所以引出了第一个区别,String可以直接赋值和用构造方法,而StingBuffer和StringBuilder只能通过构造方法赋值。
第二个区别:
public static void main(String[] args) {
StringBuffer str2=new StringBuffer();
str2.append(" hello").append(" haha");
System.out.println(str2);
String str="hello"+"haha";
//System.out.println(str);
}
根据代码也可看出String是以+号链接,StringBuffer和StringBuilder是通过append()方法链接.
第三个区别
public static void main(String[] args) {
StringBuffer str2=new StringBuffer();
str2.append(" hello").append(" haha");
System.out.println(str2);
System.out.println(str2.reverse());
String str="hello"+"haha";
//System.out.println(str);
}
String功能有的,StringBuffer和StringBuilder都有,但是StringBuffer和StringBuilder功能有的,String没有,就如上面的代码,StringBuffer和StringBuilder有reverse()方法,用起来比较方便
我们再深层次的了解下String
通过反编译可以了解到右上角的代码底层是怎么实现的,先是new了一个StringBulider对象,然后空的构造方法,再是调用append()方法,最后是toString()方法,也就是说可以写成下面的代码:
public static void main(String[] args) {
StringBuilder str=new StringBuilder();
str.append("hello").append(" haha").toString();
System.out.println(str);
}
通过底层我们发现代码被优化了,那么为什么会被优化尼??
由图可知被优化后,只会在new这个对象里进行改变,不会一直创建新的对象。
所以说String是不可变的,StringBuilder是可变的
由图可知StringBuffer前面有一个sunchronized修饰,代表保证线程安全的,适合于多线程,而没有Synchronized修饰适合单线程情况。