JAVA学习记录7——常用类(OBject类、包装类、String类、StringBuilder和StringBuffer)
一、JavaBean
实体类:在现实世界中能找到实际的个体
JavaBean(或PoJo,Domain)是一种特殊类 Javabean可以理解成实体类,其对象可用于在程序中封装数据。可以将其多个对象再封装到集合类中存储。
标准Javabean须满足以下要求:
-
成员变量用private修饰 提供每一个成员变量对应的setXX() / getXX() 方法 必须提供一个无参构造器(有参构造器可选) 需要无参构造器的原因:反射需要
二、Object类
所有类的超类,其中方法所有类都可调用。
1.equals方法
== 和 equals 的对比:
==既可判断基本类型,又可判断引用类型。如果判断基本类型,判断的是值是否相等;如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象。
equals:Object类中方法,只能判断引用类型 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等(如:String类中重写equals,用于判断字符串内容是否相同)
"abc".equals("abc"); //true
可仿照equals方法源码,在自定义类中重写,重写equals的内部仍可应用原本的equals方法判断
public boolean equals(Object anObject) { if (this == anObject) { return true; //如果是同一对象,直接返回true } if (anObject instanceof String) { //是此类才比较 String anotherString = (String)anObject; //向下转型,因为要用到anObject的属性 int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
2.hashCode方法
返回对象的哈希码值(十进制)。 对象名.hashCode()
特点:
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值必然一样
- 两个引用,如果指向的是不同对象,则哈希值不一样
- 哈希值主要根据地址号而来,不能完全等价于地址
- 在集合中如果需要,会进行重写
3.toString方法
返回该对象的字符串表示
默认返回:全类名(包名+类名)+@+哈希值的十六进制 子类往往重写toString方法,用于返回对象的属性信息
重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式
当直接输出一个对象时,toString方法会被默认的调用 System.out.println(对象);等同于调用输出 对象.toString()
4.finalize方法
- 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做自己的业务和逻辑(如释放资源,数据库连接,打开文件)
- 什么时候被回收:当某个对象没有任何引用时,JVM就认为这是一个垃圾对象,会使用垃圾回收机制来销毁对象,销毁之前,会先调用finalize方法
- 垃圾回收机制的调用由系统决定(有自己的GC算法),也可通过System.gc() 主动触发垃圾回收机制
三、包装类
1.基本介绍
针对八种基本数据类型相应的引用类型,就是包装类(Wrapper)。有了类的特点,就可以调用类中的方法
其中Byte、Short、Integer、Long、Float、Double都是Number类的子类,Number和Boolean、Character都是Object的子类
2.包装类和基本数据类型的转换
装箱:基本数据类型 —> 包装类 拆箱:包装类 —> 基本数据类型
jdk5前的手动装箱和拆箱
//手动装箱 int -> Integer int n1 = 100; Integer integer = new Integer(n1); Integer integer1 = Integer.valueOf(n1); //手动拆箱 Integer -> int int i = integer.intValue();
jdk5后(含jdk5)的自动装箱和拆箱 自动装箱底层调用的是valueOf方法,如Integer.valueOf()
int n1 = 100; Integer integer = n1; //可直接赋值,本质不变 int n2 = integer;
3.包装类型和String类型的转换
包装类 —> String:
- 直接加" "转换,对原数据的类型无影响 🌰eg:
Integer i = 100; String str1 = i + "";//i仍是Integer
- toString方法 String str2 = i.toString();
- String.valueOf String str3 = String.valueOf(i);
String —> 包装类:
String str = "123"; //parseInt方法 Integer i = Integer.parseInt(str);//使用到自动装箱 //创建对象 Integer i1 = new Integer(str);//用构造器
4.常用方法
Integer类: Integer.MIN_VALUE 返回最小值 Integer.MAX_VALUE 返回最大值
易错案例:
Character类:
四、String类
1.String的不可变性
String是字符串类型,可以定义字符串变量指向字符串对象。用Unicode字符编码。字符串内容用String中的属性 private final char value[];存放字符串内容
String类实现了Serializable接口,说明String可以串行化(串行化意味着对象可以在网络传输);实现了Comparable接口,说明了String对象可以比较
String是final类,创建的字符串对象(地址)不可改变," “方式给出的字符串对象,在堆中的字符串常量池中存储。每次修改其实是产生并指向了新的字符串对象,原对象没有改变 🌰eg:下面输出name为ABCD,但字符串常量池中存有"AB"和"CD”,name指向相加的结果
String name = "AB"; name += "CD"; System.out.println(name);
2.创建字符串对象的两种方式
- 直接用" "形式定义
- 通过String类的构造器创建
new String(char[] a,int startIndex,int count)
String s1 = new String(); String s2 = new String("ABC"); //前两种几乎不用 char[] chars = { A,B,C}; String s3 = new String(chars); byte[] by = { 97,98,99,65,66,67}; String s4 = new String(by); //转成字符串输出
区别:
-
用" "方式给出的字符串对象,在字符串常量池中存储,而且相同内容只会在其中存储一份。创建时若常量池已有,则直接指向 通过构造器new对象,每次new都会产生一个新对象,放在堆内存中,里面维护了value属性,指向常量池中相应内容的数据空间(没有就创建,有就直接指向)
char[] chars = { A,B,C}; String s3 = new String(chars); String s2 = "ABC"; System.out.println(s2 == s3); //s2,s3指向不同 输出false System.out.println(s2.equals(s3)); //比较字符串值 输出true System.out.println(s2 == s3.intern()); //s3指向堆,s3.intern指向常量池 输出true System.out.println(s3 == s3.intern()); 输出false
Person p1 = new Person(); p1.name = "abc"; Person p2 = new Person(); p2.name = "abc"; System.out.println(p1.name == p2.name); //p1,p2是不同对象,但其中的name属性均指向常量池中的abc,所以相同 输出true System.out.println(p1.name == "abc"); //属性在堆中存储,指向常量池,与"abc"指向相同 输出true String s1 = new String("abc"); String s2 = new String("abc"); System.out.println(s1 == s2); //s1,s2都指向常量池中同一地址,但s1,s2本身是两个对象,地址不同 输出false
🌰eg:
String s2 = new String("ABC");//实际上创建了两个对象,在常量池和堆区中,s2指向堆区中的 String s1 = "abc"; //实际上创建了0个对象,因为原本要在常量池创建,但已有重复字符串,s1指向常量池中的 //s1 != s2
字符串相加的实现: " "常量相加在池中
String s1 = "abc"; String s1 = "a"+"b"+"c"; //Java存在编译优化机制,编译时"a"+"b"+"c"会转化为"abc",提高运行性能 //反编译可验证 s1 == s2 true!!!
变量相加在堆中
String s1 = "hello"; String s2 = "abc"; String c = s1 + s2;
c指向堆中的value,value指向池中的"helloabc"(相当于用new String创建的对象) toString()方法的底层就是new String对象
3.字符串比较
用==判断会出错,因为" “创建的对象指向常量池,输入的字符串指向堆区 可用String类提供的equals比较 equalsIgnoreCase可忽略大小写比较
String name = "abc"; String name1 = "ABC"; name.equals(name1); //return false name.equalsIgnoreCase(name1); //return true
4.String常用API
toUpperCase、toLowerCase、compareTo与char相同。 遍历字符串中字符:
String str = "ABCDE"; for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); System.out.println(ch); }
intern() 方法: intern() 方法返回字符串对象的规范化表示形式。
它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
返回值:字符串常量池中与传入对象内容相同的字符串地址。若无相同的,则将此String对象添加到池中,并返回此对象的引用
indexOf:获取字符在字符串中第一次出现的索引,从0开始,找不到则返回 -1 lastIndexOf:获取字符在字符串中最后一次出现的索引,从0开始,找不到则返回 -1
trim:去前后空格
concat:拼接字符串,可连续拼接 String s1 = s1.concat("abc").concat("def");
format:格式化字符串%s、%c、%d、%f
五、StringBuilder和StringBuffer
String类每次更新、拼接需要重新开辟空间,效率较低,StringBuilder和StringBuffer可增强String的功能,提高效率。
StringBuffer
1)基本介绍
StringBuffer代表可变的字符序列,可对字符串内容进行增删(长度可变),StringBuffer是一个容器
StringBuffer本身是final类,不可被继承。在StringBuffer的父类AbstractStringBuilder中有属性 char[] value(不是final),存放字符串内容,因此存放在堆中。StringBuffer的增删不用每次都创建新对象,更高效
2)StringBuffer的构造器
3)String和StringBuffer的转换
String —> StringBuffer
String s = "hello"; //方式一:使用构造器 StringBuffer b1 = new StringBuffer(s); //方式二:使用append方法 StringBuffer b2 = new StringBuffer(); b2.append(s);
StringBuffer —> String
//方式一:使用toString方法 String s1 = b1.toString(); //方式二:构造器 String s2 = new String(b1);
4)StringBuffer类的常用方法
- 增——append()
- 删——delete(start,end)
- 改——replace(start,end,string)将[start,end)间的内容替换
- 查——indexOf()查找子串在字符串第一次出现的索引
- 插入——insert(index,string)在index处插入string,原索引为index的内容自动后移
- 获取长度——length()
StringBuilder
一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步(不是线程安全的)。该类被设计用作StringBuffer的一个简易替换。
StringBuilder的方法没有做互斥的处理,即没有synchronized关键字,因此用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先用此类,因为在大多数实现中,它比StringBuffer快。
StringBuilder中主要操作为append和insert方法,可重载这些方法以接受任意类型的数据
String、StringBuffer和StringBuilder的比较
-
String:不可变字符序列,效率低,复用率高 StringBuffer:可变字符序列,效率较高(增删),线程安全 StringBuilder:可变字符序列,效率最高,线程不安全
String s = "a"; s +="b"; 丢弃了原对象,产生了字符串"ab",s指向新对象"ab"。多次改变串内容,会导致大量副本字符串对象留在内存中,降低效率。 对字符串做大量修改不要用String。很少修改,被多个对象引用使用String(如配置信息)