一、String类-------java.lang.String类
1.String类的两种实例化方式
(1)直接赋值,自动入池。如果池中没有,则采用手工入池(本地方法):public native String intern();
//直接赋值,在堆上开辟空间
String str = "hello";
(2)传统方法(构造方法实例化)
//通过构造方法实例化String对象,new会新开辟一块空间。
String str1 = new String("hello");
2.字符串相等比较(比较内容)
- 用equals()成员方法
- pubilc boolean equals(String anotherString):成员方法
str1.equals(anotherString);
"=="本身是进行数值比较的,如果现在用于对象比较,那么比较的是两个对象所保存的内存地址的数值。
面试题:请解释String类的“==”与“equals”的区别
1.“==”:进行的数值比较,比较的是两个字符串对象的内存地址数值。
2.“equals()”:可以进行字符串内容的比较。
3.字符串常量(" ")是String的匿名对象
public class Test{
public static void main(String[] args){
int i1 = 10;
int i2 = 10;
System.out.println(i1 == i2);//true
String str1 = "hello";
String str2 = new String("hello");//开辟了新空间
System.out.println(str1 == str2);//false:比较的是地址,str2开辟了新空间,因而为false。
System.out.println(str1.equals(str2));//true:比较的是内容。
}
}
tips:以后在开发中,如果要判断用户输入的字符串是否等同于特定字符串,一定要把特定字符串(String常量)写在前面,避免NullPointerException
4.1.String类的两种实例化方式:
(1)直接赋值:
String str1 = "hello";
String str2 = "hello";
String str3 = "hello";
System.out.println(str1==str2);//true
System.out.println(str2==str3);//true
System.out.println(str1==str3);//true
再一次直接赋值不会开辟新的堆内存空间。而是调用同一块堆内存中的内容。如下图:
这是因为String类的设计采用了共享设计模式,如下:
在JVM底层会自动维护一个字符串的对象池(对象数组),如果现在采用直接赋值的形式进行String的对象实例化,该对象会自动保存在这个对象池之中。如果下次继续使用直接赋值的模式声明String对象,此时对象池中若有指定内容,则直接引用;如果没有,则开辟新的堆空间后将其保存在对象池中供下次使用。**
所谓的对象池就是一个对象数组(目的是减少开销)
(2)采用构造方法赋值->是标准做法。
String str = new String("hello");
String str1 = new String("hello");
分析:使用String构造方法会开辟两块堆内存空间,其中一块堆内存将成为垃圾空间,并且会产生字符串共享问题。
//该字符常量并没有保存在对象池中
String str1 = new String("hello");
String str2 = "hello";
System.out.println(str1 == str2);//false
而要想入池,可采用手工入池方法:public String intern();
String str1 = new String("hello").intern();//手工入池
String str2 = "hello";
System.out.println(str1==str2);//true
手工入池后,两个对象指向的是同一块堆内存空间。而另一块存储着"hello"的空间作为了垃圾空间。
面试题:请解释String类中两种对象实例化的区别:
①直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
②构造方法:会开辟两块空间,其中一块成为垃圾空间,不会自动保存在对象池中,但可以使用intern()方法手工入池。
5.字符串常量不可变更
所有语言对字符串的实现都是字符数组,而数组最大的缺陷就是长度固定。定义字符串常量时,内容不可改变。
字符串常量一旦定义后不可改变。变更的是对象/引用。
如:
String str = "hello";
str=str+"world";
str += "!!!";
System.out.println(str);//hello world!!!
分析:可以发现字符串没有发生变化,而是对象的引用一直在变化,因此会形成大量的垃圾空间。在开发过程中如果出现如下代码会产生大量垃圾空间:
String str = "hello";
for(int x =0;x<1000;x++){
str =+x;
}
System.out.println(str);
此代码会产生1999个垃圾空间,不利于实际开发。
注意:在实际开发过程中,尽量少出现"
+"操作,不要超过三次。
原则:
①字符串使用,采用直接赋值。
②字符串比较,用equals()实现。
③字符串别改变太多次。
6.字符与字符串的相互转换
(1)将字符数组char[ ]—> String
public String(char[ ] value)
public String(char[ ] value,int offset,int count)
(2)将字符串转为单个字符
public char charAt(int index);
(3)将字符串变为字符数组:String -> char[ ]
public char[ ] toCharArray();//通过对象调用
取得字符串长度:public int length();
7.字节(byte)与字符串
(1)将字节数组转为字符串(*******)
byte[ ] -> String
public String(byte[ ] value)
public String(byte[ ] value,int offset,int count)
(2)将字符串转为字符数组(*******)
String -> byte[ ]
public byte[ ] getBytes();
(3)将字符串按照指定编码转为字节数组
public byte[ ] getBytes(String charsetName);
8.字符串比较
(1)不区分大小写相等比较
public boolean equalsIgnoreCase(String anotherString)
(2)比较两个字符串大小
public int compareTo(String anotherString)//只要有不一样,就立即返回
- 返回大于0:表示大于比较对象
- 返回等于0:两者相等
- 返回小于0:表示小于比较对象
9.字符串查找
- public boolean contains(String str);//判断str在本字符串中是否存在
- public boolean startsWith(String str);//判断是否以指定字符串开头
- public boolean startsWith(String str,int index);//从指定位置开始判断是否以指定字符串开头
- public boolean endsWith(String str);//判断是否以指定字符串结尾
10.字符串替换
- public String replaceAll(String regex,String replacement);//替换所有内容
- public String replaceFirst(String regex,String replacement);//替换首个内容
11.字符串拆分,如果未得到结果,则需要转义\
- public String[] split(String regex);//将字符串按照指定格式全部拆分
- public String[] split(String regex,int limit);//将字符串部分拆分,数组长度为limit
12.字符串截取
- public String substring(int beginIndex):从指定位置截取到字符串结尾
- public String substring(int beginIndex,int endIndex):截取部分内容 结果是[ 0,5 )。
13.String类的其他方法
- 去掉左右空格 public String trim();
- 转大小写
public String toUpperCase();
public String toLowerCase();
- 判断字符串是否为空(只能判断是否为空字符串,而不是null)
public boolean isEmpty();
//如果为null,则运行时异常(空指针异常)。
14.两只sb(面试),方便进行字符串修改-----------java.lang自动导入
- StringBuffer:采用同步处理,线程安全,效率较低
- StringBuilder:异步处理,线程不安全,效率较高,String"+"底层会将String->StringBuilder
- String的内容不可修改,两只sb可以改内容(append),频繁修改字符串的情况建议使用StringBuffer。
字符串修改 public StringBuffer append(各种数据类型)
StringBuffer <-> String的相互转换:
①String -> StringBuffer:调用StringBuffer的构造方法或append()方法
②StringBuffer->String :StringBuffer.toString();
字符串反转 public StringBuffer reverse();
删除指定范围的数据 public StringBuffer delete(int start,int end);
插入数据 public StringBuffer insert(int offset,各种数据类型);
面试题:请解释String、StringBuffer、StringBuilder的区别:
①String的内容不可更改,StringBuffer和StringBuilder的内容可以更改。
②StringBuffer采用同步处理,属于线程安全操作;StringBuilder采用异步处理,属于线程不安全操作。
二、Object类(RealFather)— Object类是java默认提供的类。
java除了Object类,所有的类都存在继承关系,默认会继承Object父类,所有类对象都可以通过Object类进行接收。
1.取得对象信息—toString()
直接使用对象输出,默认输出一个地址编码。如果现在输出的是String对象,此时输出字符串内 容而非地址,原因在于String类覆写了Object的toString()方法。
- System.out.println();系统输出默认会调用对象的toString()方法。
- 如果默认给出的toString()功能不足(不想输出地址而是输出本类信息),需要在子类中覆写toString()方法。
默认toString()方法功能不足,则需要覆写toString()方法。
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名:"+this.name+",年龄:"+this.age;
}
}
class Student{}
public class Test{
public static void main(String[] args){
fun(new Person("roman",18));
fun("ok");
}
public static void fun(Object obj){
System.out.println(obj.toString());
}
}
运行结果:
姓名:roman,年龄:18
ok
2.对象比较—equals()
String类对象的比较使用的是equals()方法,实际上String类的equals()方法就是覆写的Object类中的equals()方法。
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名:"+this.name+",年龄:"+this.age;
}
@Override
public boolean equals(Object obj) {
if(obj == null){
return false;
}
if (this == obj)
return true;
//不是Person类对象
if (!(obj instanceof Person))
return false;
Person person = (Person) obj;//向下转型,比较属性值
return this.age == person.age &&
this.name.equals(person.name);
}
}
class Student{}
public class Test{
public static void main(String[] args){
Person per1 = new Person("roman",18);
Person per2 = new Person("roman",18);
System.out.println(per1.equals(per2));//true
}
}
3.接收引用数据类型:
在之前已经分析了Object可以接收任意的对象,因为Object是所有类的父类,但是Obejct并不局限于此,它可以接 收所有数据类型,包括:类、数组、接口。
//Object接收数组
Object obj = new int[]{1,3,4,5};
int[] data = (int[])obj;
for(int i:data){
System.out.print(i+"、");
}
Object可以接收接口是java中的强制要求,因为接口本身不能继承任何类。
Object接收接口对象:
interface IMessage{
public void getMessage();
}
class MessageImp implements IMessage{
@Override
public String toString() {
return "I am a biter";
}
@Override
public void getMessage() {
System.out.println("hello bit");
}
}
public class Test{
public static void main(String[] args) {
IMessage msg = new MessageImp();//子类向父接口转型
Object obj = msg;//接口向Object转型
System.out.println(obj);
IMessage tmp = (IMessage)obj;
tmp.getMessage();
}
}
运行结果:
I am a biter
hello bit
三、包装类
定义:将基本数据类型封装到类中。
Object类可以接收所有引用数据类型。然而在java中分为基本数据类型和引用数据类型。那么谁来管基本数据类型呢?在java中使用包装类封装基本数据类型。
1.分类:
(1)对象性包装类(Object的直接子类):Boolean(boolean)、 Character(char)
(2)数值型包装类(Number的直接子类),存在数值转换异常:
Byte(byte)、Short(short)、Long(long)、Double(double)、Float(float)、Integer(integer)
2.装箱与拆箱-------基本数据类型与相应包装类的数据处理
(1)装箱:将基本数据类型变为包装类对象,利用每个包装类提供的构造方法实现装箱。
(2)拆箱:将包装类中包装的基本类型值取出,利用Number类提供的xxValue()实现拆箱处理。
Integer num = new Integer(55);//手工装箱
int data = num.intValue();//手工拆箱
System.out.println(data);
JDK1.5后,提供了自动拆装箱的机制,这种机制可以直接利用包装类的对象进行各种数学计算。
//自动装箱
Integer x = 55;
//可以直接利用包装类对象操作
System.out.println(++x * 5);
上述代码中,这个时候依然存在有"=="和"equals"问题。
以下为问题解决的途径:
阿里编码规范:所有相同类型的包装类对象之间的值比较,全部使用equals方法比较。
注意:对于Integer var = ? 在-128~127之间的复制,Integer对象在Integer常量池产生,会复用已有对象,这个区间内的Integer值可以直接使用 == 判断。除此之外的所有数据,都会在堆上产生,并不会复用已有对象。
public class Test{
public static void main(String[] args){
Integer i1 = 10;//自动装箱
Integer i2 = new Integer(10);//手工装箱
Integer i3 = 10;
Integer i4 = 200;
Integer i5 = 200;
System.out.println(i1 == i2);//false:i2创建了一个新空间,==比较的是地址,所以i1和i2的地址不同
System.out.println(i1 == i3);//true:已在Integer常量池中产生,可以复用已有对象。
System.out.println(i4 == i5);//false:Integer对象的值不在-128~127之间,不可以用 == 判断
}
}
阿里编码规范:使用int还是Integer?
【强制】 所有POJO类(简单Java类,Bean)属性必须使用包装类型
【强制】 RPC方法的返回值和参数必须使用包赚类型
【推荐】所有的局部变量使用基本类型
3.字符串与基本类型转换
(1)将字符串转为基本类型(静态方法)
调用各个包装类.parseXX(String str)
如:
Integer.parseInt(str);
Double.parseDouble(str);
字符串转为数字时,字符串的组成有非数字,那么转换就会出现错误NumberFormatException。而字符串与boolean转换就不受影响。
public class Test{
public static void main(String[] args){
String str = "123";
int i = Integer.parseInt(str);
System.out.println(i+10);
String str1 = "123.10";
double b = Double.parseDouble(str1);
System.out.println(b+10);
}
}
运行结果:
133
133.1
(2)基本类型变为字符串:
①任何数据类型使用 +" " 就变成了字符串类型。
②使用String类的valueOf()方法,此方法不产生垃圾空间(推荐)。
例:
String str = String.valueOf(100);
System.out.println(str);