今天来总结一下Java中的三大特殊类:String类, Object类, 和包装类
一,String类
1. String类的两种实例化方式
★ 直接赋值 :
String str = "Hello World" ; // str是一个对象,那么"Hello World" 就应该保存在堆内存中
System.out.println(str) ;
★ 传统方法(构造法):
String str = new String("Hello Bit") ;
System.out.println(str) ;
● 两种实例化的区别:
1).采用直接赋值:
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true
采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中。如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用;如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用。
所谓的对象池就是一个对象数组(目的就是减少开销)
2).采用构造方法
String str = new String("hello") ;
如果使用String构造方法,则在程序编译期会先去字符串常量池检查,是否存在“hello”,如果不存在则开辟一块空间存放“hello”,存在的话就不会开辟,然后在堆内存中开辟一块空间来存放new出来的String实力,再在战中开辟一块空间命名为str,存放堆内存中String实例的地址。
在String类中提供有方法入池操作 public String intern() ;
// 该字符串常量并没有保存在对象池之中
String str1 = new String("hello") ;
String str2 = "hello" ;
System.out.println(str1 == str2); // false
//方法入池后
String str1 = new String("hello").intern() ;
String str2 = "hello" ;
System.out.println(str1 == str2); // true
总结:
String类中两种对象实例化的区别 :
1). 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
2). 构造方法:会开辟两块堆内存空间,其中一块成为垃圾空间,不会自动保存在对象池中,可以使用 intern()方法手工入池。
因此,我们一般会采取第一种方式即直接赋值
String类的其中一种构造方法如下: public String(String str) ;
2, 字符串相等比较
观察以下代码的输出
int x = 10 ;
int y = 10 ;
System.out.println(x==y); //true
String str1 = "Hello" ;
String str = new String("Hello") ;
System.out.println(str1==str); // false
为什么两个字符串内容相同,而使用 == 得到的结果不同。
来看内存图分析:
可知:== 本身是进行数值比较的,若用于对象比较,则比较的是两个对象所保存的内存地址是否相等,而并没有比较对象的内容。
若想要进行内容比较,则必须采用String类提供的equals方法:
String str1 = "Hello" ;
String str = new String("Hello") ;
System.out.println(str1.equals(str));
由此可知:
==比较与equals比较的区别是:
(1) ==:进行数值比较,比较的是两个字符串对象的内存地址数值。
(2) “equals()”:可以进行字符串内容的比较
3,字符串不可变更
字符串一旦定义不可改变。
判断如下代码共开辟了多少块内存空间:
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str); // hello world!!!
一起来看看这段代码内存模型:
可以发现字符串上没有发生任何变化,但是字符串对象的引用一直在改变,而且会形成大量的垃圾空间。因此我们应该尽量避免在代码中用循环对字符变量的引用不断赋值。
关于字符串的一些方法:
方法名称 | 类型 | 描述 |
public boolean equals(Object anobject) | 普通 | 区分大小写的比较 |
public boolean equalsIgnoreCase(String anotherString) | 普通 | 不区分大小写的比较 |
public int compareTo(String anotherString) | 普通 | 比较两个字符串大小关系 |
public boolean contains(CharSequence s) | 普通 | 判断一个子字符串是否存在 |
public int indexOf(String str) | 普通 | 从头开始查找指定字符串的位置,查到了返回位置的开始索引,查不到返回-1 |
public int indexOf(String str,int fromIndex) | 普通 | 从指定位置开始查找子字符串位置 |
public int LastIndexof(String str) | 普通 | 由后向前查找子字符串位置 |
public int LastIndexof(String str,int fromIndex) | 普通 | 从指定位置由后向前查找 |
public boolean startsWith(String prefix) | 普通 | 判断是否由指定字符串开头 |
public boolean startsWith(String prefix,int toffset) | 普通 | 从指定位置开始判断是否以指定字符串开头 |
public boolean endsWith(String suffix) | 普通 | 判断是否以指定字符串结尾 |
public String(byte bytes[]) | 构造 | 将字节数组变为字符串 |
public String(byte bytes[],int offset,int Length) | 构造 | 将部分字节数组中的内容变为字符串 |
public byte[] getBytes() | 普通 | 将字符串以字节数组的形式返回 |
public String(char value[]) | 构造 | 将字符数组中的所有内容变为字符串 |
public String(char value[],int offset,int count) | 构造 | 将部分字符数组中的内容变为字符串 |
public char charAt(int index) | 普通 | 取得指定索引位置的字符,索引从0开始 |
public char[] toCharArray() | 普通 | 将字符串变为字符数组返回 |
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) | 普通 | 将字符串部分拆分,该数组长度就是limit极限 |
public String substring(int beginIndex) | 普通 | 从指定索引截取到结尾 |
public String substring(int beginIndex,int endIndex) | 普通 | 截取部分内容 |
public String trim() | 普通 | 去掉字符串中的左右空格,保留中间空格 |
public String toUpperCase() | 普通 | 字符串转大写 |
public String toLowerCase() | 普通 | 字符串转小写 |
public native String intern() | 普通 | 字符串入池操作 |
public String concat(String str) | 普通 | 字符串连接,等同于“+”,不入池 |
public int Length() | 普通 | 取得字符串长度 |
public boolean isEmpty() | 普通 | 判断是否为空字符串,但不是null,而是长度为0 |
4, StringBuffer类
String类的特点:任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。
由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer类。 在String中使用"+"来进行字符串连接,但在StringBuffer类中需要更改为append()方法:
public synchronized StringBuffer append(各种数据类型 b)
来看看StringBuffer的使用:
public class Stringbuffer {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("Hello").append("World");
fun(sb);
System.out.println(sb);
}
public static void fun(StringBuffer temp) {
temp.append("\n").append("welcome to china");
}
}
输出结果如下:
"C:\Program Files\Java\jdk1.8.0_131\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.1\lib\idea_rt.jar=28563:C:\Program Files\JetBrains\IntelliJ IDEA 2018.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;D:\code\java\Base\target\classes" String.Stringbuffer
HelloWorld
welcome to china
Process finished with exit code 0
String和StringBuffer大的区别在于:String的内容无法修改,而StringBuffer的内容可以修改。频繁修改字符串 的情况考虑使用StingBuffer。
两个类都是"CharSequence"接口的子类。
注意:String和StringBuffer类不能直接转换。
如果要想互相转换,可以采用如下原则:
●String变为StringBuffer:利用StringBuffer的构造方法或append()方法
● StringBuffer变为String:调用toString()方法。
除了append()方法外,StringBuffer也有一些String类没有的方法:
★字符串反转:
public synchronized StringBuffer reverse()
★删除指定范围的数据:
public synchronized StringBuffer delete(int start, int end)
★插入数据:
public synchronized StringBuffer insert(int offset, 各种数据类型 b)
二,Object类
Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父 类。即,所有类的对象都可以使用Object进行接收。
方法名称 | 类型 | 描述 |
public Object() | 构造 | 取得无参构造为子类服务 |
public String toString() | 普通 | 取得对象信息 |
public boolean equals(Object obj) | 普通 | 进行对相比较 |
在使用对象直接输出的时候,默认输出的是一个地址编码。如果现在使用的是String类,该类对象直接输出的是内容,主要原因就是toString()方法的问题。默认Object类提供的toString()方法只能够得到一个对象的地址(而这是所有对象都共同具备 的特征)。
toString()的核心目的在于取得对象信息。 String作为信息输出的重要数据类型,在Java中所有的数据类型只要遇见了String并且执行了 + ,那么都要求将其变 为字符串后连接,而所有对象要想变为字符串就默认使用 toString() 方法。
2. 对象比较
String类对象的比较使用的是equals()方法,实际上String类的equals()方法就是覆写的Object类中的equals()方法。
3. 接收引用数据类型
现在知道Object可以接收任意的对象,因为Object是所有类的父类,但是Obejct并不局限于此,它可以接收所有数据类型,包括:类、数组、接口。Object可以接收接口是Java中的强制要求,因为接口本身不能继承任何类。
来看一个Object类接受接口的例子:
interface IMessage {
public void getMessage() ;
}
class MessageImpl implements IMessage {
@Override
public String toString() {
return "I am small girl" ;
}
public void getMessage() {
System.out.println("中国欢迎你");
}
}
public class Test {
public static void main(String[] args) {
IMessage msg = new MessageImpl() ; // 子类向父接口转型
Object obj = msg ; // 接口向Obejct转型
System.out.println(obj);
IMessage tem = (IMessage) obj ; // 强制类型转换
temp.getMessage();
}
}
Object真正达到了参数的统一,如果一个类希望接收所有的数据类型,就是用Object完成。
三 ,包装类
在Java中,数据类型分为基本数据类型和引用数据类型,Object类可以接收所有引用数据类型。那么基本数据类型如何处理呢?
1, 基本原理
包装类就是将基本数据类型封装到类中。
利用IntValue就可以实现基本数据类型变为对象的需求。将基本数据类型包装为一个类对象的本质就是使用Object进行接收处理。
●数值型(Number的子类):Byte,Short,Integer(int),Long,Float,Double
● 对象型(Object的子类):Boolean,Character(char)
2, 装箱与拆箱
在包装类与基本数据类型处理之中存在有两个概念:
●装箱:将基本数据类型变为包装类对象,通过每个包装类的构造方法实现装箱处理。
●拆箱:将包装类中包装的基本数据类型取出,利用包装类提供的XxValue()方法。
例如:
public class Test {
public static void main(String[] args){
//装箱
Integer integer=new Integer(10);
//拆箱
int data=integer.intValue();
System.out.println(data+10);
}
}
Java在JDK1.5之后就提供了自动拆装箱机制,使用包装类与使用基本类型在用法上基本一致。
使用基本类还是包装类?
1)【强制】所有POJO类(类中只有属性与getter/setter,构造方法)使用包装类。
2)局部变量推荐使用基本类。包装类进行数值比较使用equals方法。
对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在IntegerCache.cache 产生,会复用 已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产 生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
3. 字符串与基本数据类型转换
如何将字符串变为各个数据类型,这个时候就需要包装类支持。
字符串---->基本数据类型
★ String变为int 类型(Integer类):public static int parseInt(String s) throws NumberFormatException
★ String变为double类型(Double类):public static double parseDouble(String s) throws NumberFormatException
★ String变为Boolean类型(Boolean类):public static boolean parseBoolean(String s)
eg:将字符串变为int 型
String str = "123" ; // String类型
int num = Integer.parseInt(str) ;
System.out.println(num);
注意:将字符串转为数字的时候,字符串的组成有非数字,那么转换就会出现错误 (NumberFormatException)
基本数据类型---->字符串:
★任何数据类型使用了"+"连接空白字符串就变为了字符串类型。
★使用String类中提供的valueOf()方法,此方法不产生垃圾。
eg:
String str = String.valueOf(100) ;
System.out.println(str);