常用类解析
9.1 Object类
Object是所有类的最终父类,每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法。
9.1.1 Object类常用方法介绍
-
public final native Class<?> getClass();
相当于获取对象最本质的数据类型 -
public native int hashCode();
返回对象的哈希码的值(如果该类没有重写hashCode)
hashCode默认值就是对象在堆内存中的真实物理地址 -
public boolean equals(Object obj) {
return (this == obj);
}
Object本身equals比的是自身对象this和传入对象obj的地址值。Object类中的 equals方法内部使用的就是= =比较运算符。
如需重新定义等于,则子类重写,(hashCode方法也有必要重写)按需比较即可。
注意:在复写Object中的equals方法时,一定要注意public boolean equals(Object obj)的参数是Object类型,在调用对象特有数据时,一定要进行类型转换,在转换之前必须进行类型判断。
-
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
Object本身的toString返回的是对象最本质的数据类型的名称+"@"+哈希值的十六进制形式。
如需重新定义对象的字符串形式,则子类重写,按需拼接数据即可 -
protected native Object clone() throws CloneNotSupportedException
对象的拷贝
对于任何对象 x
表达式: x.clone() != x为 true
【保证肯定有新对象创建】
表达式: x.clone().getClass() == x.getClass()也为 true,但这些并非必须要满足的要求。
【保证对象和副本之间的数据类型是一致的】
一般情况下: x.clone().equals(x)为 true,但这并非必须要满足的要求。
【保证对象和副本之间的数据内容是一致的】
注意:所有的数组都被视为实现接口 Cloneable。Object 类本身不实现接口 Cloneable,所以在类为Object 的对象上调用 clone 方法将会导致在运行时抛出异常。
package usualclassDemo;
public class ObjectDemo {
public static void main(String[] args) {
String string = "abc";
//.getClass()返回此对象的运行时类
System.out.println(string.getClass());
Animal a1 = new Dog();
Animal a2 = new Cat();
System.out.println(a1.getClass());
System.out.println(a2.getClass());
System.out.println("----------");
Animal a3 = new Animal();
Animal a4 = new Animal();
//hashcode相同,但对象地址不同
System.out.println(a3.hashCode());
System.out.println(a4.hashCode());
//打印对象,其实调用的是类中的toString方法
System.out.println(a3.toString());
//没有重写toString的话,会返回:此对象运行时的类@对象的散列值
System.out.println(a4);
System.out.println(a3 == a4);
//此类没有重写equals方法,所以底层依旧是 == 上下两个结果相同
System.out.println(a3.equals(a4));
//无论hashCode是否重写 == 两边永远比的是对象的真实物理内存地址!
System.out.println("=========");
Person p1 = new Person("小猫", 1);
Person p2 = new Person("小狗", 1);
//调用了person类中的toString方法
System.out.println(p1);
System.out.println(p2);
System.out.println(p1.equals(p2));
System.out.println("=========");
Person p3 = new Person("滴滴", 20);
Person p4 = null;
try {
p4 = (Person) p3.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(p3);
System.out.println(p4);
System.out.println(p3 == p4);
//该类重写了equals 所以上下结果不同
System.out.println(p3.equals(p4));
p3.name = "玉米";
p3.age = 18;
System.out.println(p3);
System.out.println(p4);
}
}
class Person implements Cloneable{
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj == this) {
return true;
}
if(obj == null) {
return false;
}
//运用反射机制,先判断是不是同一个类型,在判断属性是否相同,在判断内容是都相同
if(getClass() == obj.getClass()) {
Person other = (Person) obj;
return this.name.equals(other.name)&&this.age==other.age;
}
return false;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name + ":" + age;
}
/*
* public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/
}
class Animal{
/*
* @Override public int hashCode() { // TODO Auto-generated method stub return
* 128; }
*/
}
class Dog extends Animal{}
class Cat extends Animal{}
/*
//结果:
class java.lang.String
class usualclassDemo.Dog
class usualclassDemo.Cat
----------
932583850
212628335
usualclassDemo.Animal@379619aa
usualclassDemo.Animal@cac736f
false
false
=========
小猫:1
小狗:1
false
=========
滴滴:20
滴滴:20
false
true
玉米:18
滴滴:20
*/
9.2 基本数据类型包装类
基本数据类型包装类,就是按照万事万物皆对象的思想,将我们原先的那些基本数据类型,当成类和对象来看。
byte——Byte
short——Short
int——Integer
long——Long
float——Float
double——Double
char——Character
boolean——Boolean
最主要的使用场景就是在基本数据与字符串之间的转换问题
将基本类型转字符串;将字符串转成基本类型。
将基本数值转成字符串有3种方式 :
(1)基本类型值直接与""(空串)相连接即可;99+""
(2)调用String的valueOf()方法;String.valueOf(99)
(3)调用包装类中的toString方法;Integer.toString(99)
基本类型和包装对象之间的转换
//基本数值---->包装对象
Integer i = new Integer(4);//使用构造函数函数
Integer i1 = new Integer("4");//构造函数中可以传递一个数字字符串
Integer i2 = Integer.valueOf(4);//使用包装类中的valueOf方法
//包装对象---->基本数值
int num = i.intValue();
自动装箱拆箱
JDK1.5以后,有了一个包装类的新特性。目的简化书写,自动装箱
Integer i = 4;//自动装箱。Integer i = Integer.valueOf(4);
i = i + 5;//原理;等号右边:将i对象转成基本数值 i.intValue() + 5;//自动拆箱。加法运算后,再次装箱。
package usualclassDemo;
public class IntegerDemo {
public static void main(String[] args) {
//将基本类型转换成字符串
//将该数字以默认十进制转为字符串输出
System.out.println(Integer.toString(99));
//将该数字以三进制转为字符串形式
System.out.println(Integer.toString(99, 3));
//将该数字以二进制转为字符串形式
System.out.println(Integer.toBinaryString(99));
//将该数字以十六进制转为字符串形式
System.out.println(Integer.toHexString(99));
//将该数字以八进制转为字符串形式
System.out.println(Integer.toOctalString(99));
//将数字拼接一个空串转为字符串形式
System.out.println(10010 + "");
System.out.println(String.valueOf(99));
//将字符串转成基本类型
//将字符串s默认以十进制转出来十进制整数
System.out.println(Integer.parseInt("1123"));
//将字符串s以radix进制转出来十进制整数
System.out.println(Integer.parseInt("1001",2));
System.out.println("----------");
//基本数值---->包装对象
int i1 = 1;
int i2 = 2;
Integer i3 = Integer.valueOf(3);//new Integer()使用包装类中的valueOf方法
Integer i4 = new Integer(4);//使用构造函数函数
Integer i5 = new Integer(5);
//自动装箱 自动将8封装为一个Integer对象
Integer i6 = 8; //Integer.valueOf(8) -> new Integer(8)
//自动拆箱 基本数据类型的包装类对象 一旦和基本数据类型或基包进行基本四则运算
//包装对象---->基本数值
//自动会使用 intValue();
System.out.println(i1 + i3.intValue());
System.out.println(i2 + i4.intValue());
System.out.println(i3.intValue() + i4.intValue());
System.out.println(i4.intValue() + i5.intValue());
System.out.println("+++++++++++++++++++++++++");
Integer a = 1; //new Integer(1)
Integer b = 1; //new Integer(1)
System.out.println(a == b);//true
//不是你理解a.intValue() == b.intValue()
//a == b 确实比较的是两个对象的地址
System.out.println(a.equals(b));//true
Integer x = 200; //new Integer(200)
Integer y = 200; //new Integer(200)
System.out.println(x == y); //false
//x == y 确实比较的是两个对象的地址
System.out.println(x.equals(y));//false
在jdk1.5自动装箱时,如果数值在byte范围之内,不会新创建对象空间而是使用原来已有的空间。
/*
* if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
*
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
*
*
* */
}
}
9.3 StringBuffer类、StringBuilder类
都是String类的缓冲字符串。
特点:可以改变其大小,也可进行增删改查操作的字符串。
9.3.1 String类
特点:
- 无论是字面量还是new出来的 一律都是字符串对象
- 字符串一旦创建,其长度不可改变(底层就是一个字符数组)
- 内容也不能改变(如果真的要改变内容,只能重新创建字符串)
- 字符串本质上底层就是一个不可变长度且不可变内容的字符数组!
package usualclassDemo;
public class StringDemo {
public static void main(String[] args) {
String s1 = "abc";
//↑s1、s2、s3在内存中只有一个对象,在字符串常量池中,String类是final常量,字符串常量池资源共享,不可改变
//↑创建一个对象
String s2 = "abc";
//↑常量池中已经存在对象"abc"(共享),创建0个对象,s2直接引用已经创建的对象s1的地址
String s3 = "a" + "bc";
//↑先拼接再在常量池中寻找是否有abc,有直接引用,没有就创建,创建0个对象
//↑常量的值在编译的时候就被确定(优化)了 s3等同于s1、s2
String s4 = new String("abc");
//↑创建了两个对象,一个存放在字符串池中,一个存放在堆区中;
//↑ 还有一个对象引用s4存放在栈中
String s5 = new String("abc");
//↑字符串常量池中已经存在“abc”对象,所以只在堆区中创建了一个对象
String s6 = new String("a");
String s7 = new String("bc");
//↑s4,s5,s6,s7在内存中有两个对象,一个new的对象在堆中,一个字符串本身对象,在字符串常量池中
String s8 = s6 + s7;
//↑运行期的两个string相加,会产生新的对象的,存储在堆(heap)中
String s9 = "a" + s7;
//↑JAVA编译器对string + 基本类型/常量 是当成常量表达式直接求值来优化的。
String s10 = "efg";
String s11 = "d";
//↑字符串常量池中创建一个对象
String s12 = "efgd";
String s13 = s10 + s11;
/*局部变量str10,str11存储的是存储两个拘留字符串对象(intern字符串对象)的地址。
* ↑运行期JVM首先会在堆中创建一个StringBuilder类,
* 同时用str10指向的拘留字符串对象完成初始化,
* 然后调用append方法完成对str11所指向的拘留字符串的合并,
* 接着调用StringBuilder的toString()方法在堆中创建一个String对象,
* 最后将刚生成的String对象的堆地址存放在局部变量str13中。
* 而str12存储的是字符串池中"efgd"所对应的拘留字符串对象的地址。
* s12与s13地址当然不同
* 内存中实际上有五个字符串对象:
* 三个拘留字符串对象、一个String对象和一个StringBuilder对象。
*/
System.out.println(s1 == s2);
//↑true 指向同一个对象
System.out.println(s1 == s3);
//↑true 指向同一个对象
System.out.println(s1.equals(s2));
//↑true 值相同
System.out.println(s1.equals(s3));
//↑true 值相同
//↑因为String复写了equals方法,建立字符串自己的判断相同的依据。通过字符串对象中的内容来判断的。
System.out.println(s1 == s4);
//↑false 存放的地区不同,一个栈区,一个堆区
System.out.println(s1.equals(s4));
//↑true 值相同
System.out.println(s1 == s5);
//↑false 存放的地区不同,一个栈区,一个堆区
System.out.println(s4 == s5);
//↑false s4,s5栈区的地址不同,指向堆区的不同地址
System.out.println(s1.equals(s5));
//↑true 值相同
System.out.println(s1 == s8);
//↑false
System.out.println(s4 == s8);
//↑false
System.out.println(s5 == s8);
//↑false
System.out.println(s4.equals(s8));
//↑true 值相同
System.out.println("============");
System.out.println(s1 == s2);
//↑true 同一个对象
System.out.println(s1.intern() == s2.intern());
//↑true
//s1本身存放的是字符串常量在字符串常量池中的地址.
//所以intern()的结果和s1存储的地址是一样的
//s4本身存放的是字符串对象在堆内存中的地址,
//intern()返回的是字符串对象所指向的那个字符串常量在字符串常量池中的地址
System.out.println(s1 == s4);
//↑false
System.out.println(s1.intern() == s4.intern());
//↑true
System.out.println(s4 == s5);
//↑false
System.out.println(s4.intern() == s5.intern());
//↑true
System.out.println(s4.equals(s5));
//↑true
System.out.println(s1 == s9);
//↑false
System.out.println(s12 == s13);
//↑false
}
}
9.3.2 StringBuilder类
StringBuilder继承自AbstractStringBuilder实现了CharSequence和Serializable。
AbstractStringBuilder实现了Appendable(可扩展的)接口,要实现的方法append()。
AbstractStringBuilder实现了CharSequence字符序列接口(同数据结构中List的定义)。
StringBuilder的构造函数是 创建一个默认容量为16的字符数组,随着后续内容的增加,底层在进行动态的扩容。
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
package usualclassDemo;
public class StringBuilderDemo {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
//append什么都可以添加 都转换为字符串内容进行添加在尾部
//append执行之后 返回的是sb自己
sb.append(99);
sb.append(3.14);
sb.append(false);
sb.append(new Object());
sb.append(new Integer(999));
sb.append("these");
System.out.println(sb.toString());
System.out.println(sb.capacity());
System.out.println(sb.charAt(2));
System.out.println(sb.length());
System.out.println(sb.reverse());
System.out.println(sb);
}
}
9.3.3 StringBuffer类
StringBuffer和StringBuilder基本完全一样
- StringBuffer是线程安全的——多线程环境下和单线程环境下不容易出错
StringBuilder是线程不安全——多线程环境下容易出错单线程环境下不容易出错 - 从代码的角度而言,StringBuffer里面的大部分成员函数都有synchronized关键字修饰里面的大部分成员函数都有synchronized关键字修饰
- StringBuilder就相当于一个人在家上厕所 一般不会反锁门 一般也不会判断卫生间里有没有人
StringBuffer就相当于你在火车上厕所 一般会反锁们 一般也会判断卫生间里有没有人 - 在单线程情况下 StringBuilder比StringBuffer效率高一些 因为不需要判断锁这个问题
在多线程情况下 StringBuffer比StringBuilder安全高一些 因为需要判断锁这个问题
9.4 BigInteger类和BigDecimal类
在Java中,基本数据类型中整数最长的就是long类型8字节 64位 -2^63 ~ 2^63 - 1
如果我们在实际应用中的数据超过了这个范围怎么办?
- BigInteger专门处理大整数运算;BigDecimal专门出来大小数运算
- BigInteger底层就是用一个不可变的数组来维护计算我们所存储的数据的
如果通过运算得出新的数据的话,则返回新的BigInteger的对象。 - BigDecimal底层其实是BigInteger和小数位记录scale组成的
123.4567 : BigInteger(1234567) + scale(4) = 123.4567
package usualclassDemo;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
public class BigIntegerDemo {
public static void main(String[] args) {
BigInteger b1 = new BigInteger("115456421231654561231");
BigInteger b2 = new BigInteger("2");
//加法
System.out.println(b1.add(b2));
//除法
System.out.println(b1.divide(b2));
//取余操作
System.out.println(b1.mod(new BigInteger("2")));
//乘法
System.out.println(b1.multiply(b2));
//b2.negate() = -b2
System.out.println(b1.add(b2.negate()));
System.out.println(b1.pow(2));
BigInteger b3 = new BigInteger("8102380182038012830102380123");
//将BigInteger对象的数字转成基本数据类型对应的数字
System.out.println(b3.intValue());
//强制转换 将高位的数据截取 结果是不正确不精确
/*
* ArithmeticException: BigInteger out of int range
* System.out.println(b3.intValueExact());
* 温和转换 如果出现溢出截取的情况 则直接报异常 不给出计算结果
* 能用带Exact用Exact的 更加安全一些
*/
System.out.println("=================");
BigDecimal bd1 = new BigDecimal("156.954623");
BigDecimal bd2 = new BigDecimal("1.26543000");
BigDecimal bd3 = new BigDecimal("126543000");
System.out.println(bd1.add(bd2));
System.out.println(bd1.add(bd2.negate()));
System.out.println(bd1.multiply(bd2));
//对一个 BigDecimal 设置它的 scale ,如果精度比原始值低,那么按照指定的方法进行四舍五入或者直接截断。
System.out.println(bd1.divide(bd2,10,RoundingMode.HALF_UP));
System.out.println(bd1.scale());
System.out.println(bd2.scale());
System.out.println(bd3.stripTrailingZeros());
System.out.println(bd2.stripTrailingZeros().scale());
System.out.println(bd3.stripTrailingZeros());
System.out.println(bd3.stripTrailingZeros());
//无论是BigInteger该是BigDecimal 比较大小尽量不要用equals()
BigDecimal bd4 = new BigDecimal("1.2700");
BigDecimal bd5 = new BigDecimal("1.27");
System.out.println(bd4.equals(bd5));
System.out.println(bd4.compareTo(bd5));
}
}
小结:
1.BigInteger 用于表示任意大小的整数;
2.BigInteger 是不变类,并且继承自 Number ;
3.将 BigInteger转换成基本类型时可使用 ==longValueExact()==等方法保证结果准确。
4.如果一个 BigDecimal 的 scale() 返回负数,例如, -2 ,表示这个数是个整数,并且末尾有2个0。
5.可以对一个 BigDecimal 设置它的 scale ,如果精度比原始值低,那么按照指定的方法进行四舍五入或者直接截断。
6.在比较两个 BigDecimal 的值是否相等时,要特别注意,使用 equals() 方法不但要求两个BigDecimal 的值相等,还要求它们的 scale() 相等。必须使用 compareTo() 方法来比较,它根据两个值的大小分别返回负数、正数和 0 ,分别表示小于、大于和等于。
7.实际上一个 BigDecimal 是通过一个 BigInteger和一个 scale 来表示的,即 BigInteger 表示一个完整的整数,而 scale 表示小数位数。
public class BigDecimal extends Number implements Comparable<BigDecimal>{
private final BigInteger intVal;
private final int scale;
}
9.5 正则表达式
正则表达式不仅仅是Java的技术,在任何一门编程语言中都会存在,是一种通用的IT技术。其理念和用法在任何编程语言中基本一致,除了有一些由于语言不同而导致的一些语法不同。正则表达式,主要用于匹配(查找 替换 计数)字符串中的数据的,也叫做文本匹配技术。