6.3 常见类

一、Object类——java.lang.Object

Object类是所有数组、类、枚举类的父类。当一个类没有使用extends关键字显示去继承父类。则该类默认继承Object类
常用方法:

1、''boolean equals(Object obj)''

作用:判断指定对象与该对象是否相等。
注意:String类重写了该方法,判断标准是两个对象的字符串相同,则返回true。

class EqualsTest 
{
	public static void main(String[] args) 
	{
		String s1="java";//存在于常量池中
		String s2="java";//直接指向常量池中存在的"java"字符串
		System.out.println(s1.equals(s2));//true
		System.out.println(s1==s2);//true

		String s3=new String("java");//存在于堆内存中
		String s4=new String("java");
		//String类改写了equals()方法,所以输出与==不同
		System.out.println(s3.equals(s4));//true
		System.out.println(s3==s4);//false
		System.out.println(s1==s3);//false
	}
}

2、protected void finalize():

当系统中没有应用变量引用到该应用变量时,垃圾回收器调用此方法来清理该对象的资源。

class  GcTest
{
	public static void main(String[] args) 
	{
		for (int i=0;i<4 ;i++ )
		{
			new GcTest();//创建了对象但是没有引用,等待垃圾回收机制回收
			System.gc();//强制系统进行垃圾回收
			//Rumtime.getRuntime.gc();
		}
	}
	@Override
	public void finalize()
	{
		System.out.println("系统正在清理GcTest对象的资源");
	}
}

3、Class<?>.getClass():

返回该对象运行时的类

4、int hashCode():

返回该对象的hashCode值。默认情况下,该对象的hashCode值根据对象的地址来计算(与System.indentityHashCode(Object x)计算结果相同)。但很多类重写hashCode()方法,不在根据地址来计算。

5、String toString():

返回该对象的字符串表示,当程序使用System.out.println()输出一个对象时,系统会自动调用toString()方法返回该对象的字符串表达形式。Object类toString()返回:"运行时的类名"@十六进制的hashCode值。但对象常常重写该方法,用于返回该对象信息的字符串。

class A
{
	void info()
	{
		System.out.println("这是一个方法");
	}
}
public class toStringTest 
{
	public static void main(String[] args) 
	{
		A a=new A();
		//将hashCode值转换为16进制的字符串
		System.out.println(Integer.toHexString(a.hashCode()));
		System.out.println(a.toString());
	}
}
---------- 运行Java捕获输出窗 ----------
8efb846
A@8efb846

输出完成 (耗时 0 秒) - 正常终止

6、protected Object clone()

该方法用于帮助其他对象来实现“自我克隆”,得到一个当前对象的副本,而且两者完全隔离。由于clone()使用protected修饰,因此该方法只能子类重写或调用。
步骤:
(1)自定义实现Cloneable接口。这是一个标记性的接口,实现该节口的对象可实现自我克隆。
(2)自定义类实现clone方法。
(3)实现克隆时,调用super.clone().

class Address
{
	String detail;
	public Address(String detail)
	{
		this.detail=detail;
	}
}
//1、自定义类实现Cloneable接口
class User implements Cloneable
{
	int age;
	Address address=new Address("广州天河");
	public User(int age)
	{
		this.age=age;
	}
	//2、自定义clone()方法、3、调用super.clone()
	public User clone()
		throws CloneNotSupportedException
	{
		return (User)super.clone();
	}
}
public class CloneTest
{
	public static void main(String[] args)
		throws CloneNotSupportedException
	{
		var u1=new User(29);
		//clone得到u1的副本
		var u2=u1.clone();
		//判断两个副本是否相等
		System.out.println(u1==u2);//false
		//判断u1、u2的地址是否相同
		System.out.println(u1.address==u2.address);//true
	}
}

</font color=red>Object类提供的Clone机制只是对对象中的各个实例变量进行简单的复制,如果该变量为引用类型的变量,Object的Clone机制也只是简单地赋值这个引用变量,这样原有对象的引用类型与克隆对象的引用类型依然指向内存中同一个实例。如下图:

二、操作对象的Objects工具类——java.util.Objects

Java 7新增了一个Object类,它提供了一些工具方法来操作对象,这些工具方法大多数是“空指针”安全的
提示:Java为工具类的命名习惯是添加一个字母s,比如操作数组的工具类是Arrays
操作集合的工具类是Collections
下面示范Objects工具类的用法:

import java.util.Objects;
public class ObjectsTest
{
	//定义一个obj变量,它的默认值是null
	static ObjectsTest obj;
	public static void main(String[] args)
	{
		//输出一个null对象的hashCode
		System.out.println(Objects.hashCode(obj));
		//输出一个null对象的toString,输出为null
		System.out.println(Objects.toString(obj));
		//System.out.println(obj.toString());//Exception in thread "main" java.lang.NullPointerException
		//要求obj不能为null,如果为null则引发异常
		System.out.println(Objects.requireNonNull(obj,"obj参数不能是null"));
	}
}
---------- 运行Java捕获输出窗 ----------
0
null
Exception in thread "main" java.lang.NullPointerException: obj参数不能是null
	at java.base/java.util.Objects.requireNonNull(Objects.java:246)
	at ObjectsTest.main(ObjectsTest.java:14)

输出完成 (耗时 0 秒) - 正常终止

Objects.requireNonNull()方法,当传入参数不为null时,该方法返回参数本身;否则引发NullPointerException异常,该方法主要对形参进行输入验证。
例如下面的构造器:

public Foo(Bar bar)
{
    //校验bar参数,如果bar参数为null则引发异常,否则this.bar被赋值为bar参数
    this.bar=Objects.requireNonNull(bar);
}

三、Java 9改进的String、StringBuffer和StringBuilder

★String类不可变类,即一个类创建以后,包含在这个对象的字符序列是不可改变的,直至这个对象被销毁。
★StringBuffer对象则是代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供1的append()、insert()、reverse()、SetCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成最终的字符串,就可以调用它的toString()方法将其转换为String对象。
★StringBuilder类是JDK 1.5新增的类,它代表可变的字符串对象。</font color=red>StringBuilder和StringBuffer两个类的构造器和方法基本相同。不同的是StringBuffer的线程是安全的,StringBuilder的没有实现线程安全的功能,所以性能略高。因此通常情况下,创建一个内容可变的字符串对象,则有限考虑StringBuilder.
在Java 9改进之前字符串采用char[]数组保存字符,因此字符串的每个字符占2个字节:而Java 9及更新版本的JDK采用byte[]数组再加上一个encoding-flag字段来保存字符,因此字符串的每个字符只占1个字节。
String类是不可变的,所以会额外产生一些临时变,例如String str="java"+str1;内存中会分配三个空间,一个是"java"字符串,一个是str1,一个是str。使用StringBuffer和StringBuilder可以很好地解决这个问题。
StringBuilder提供了一些插入追加、改变字符串里包含的字符序列的方法,而StringBuffer与其用法完全相同,只是StringBuffer的线程是安全的。
StringBuffer和StringBuilder的两个属性:
length表示其包含的字符序列的长度。StringBuffer和StringBuilder的length是可以改变的,可以通过length()、setLength(int len)来改变字符序列的长度
capacity属性表示StringBuffer和StringBuilder的容量,capacity通常比length大,程序无需关心capacity属性。


public class StringBuilderTest
{
	public static void main(String[] args)
	{
		var sb = new StringBuilder();
		// 追加字符串
		sb.append("java");//sb = "java"
		// 插入
		sb.insert(0, "hello "); // sb="hello java"
		// 替换
		sb.replace(5, 6, ","); // sb="hello,java"
		// 删除
		sb.delete(5, 6); // sb="hellojava"
		System.out.println(sb);
		// 反转
		sb.reverse(); // sb="avajolleh"
		System.out.println(sb);
		System.out.println(sb.length()); // 输出9
		System.out.println(sb.capacity()); // 输出16
		// 改变StringBuilder的长度,将只保留前面部分
		sb.setLength(5); // sb="avajo"
		System.out.println(sb);
	}
}
---------- 运行Java捕获输出窗 ----------
hellojava
avajolleh
9
16
avajo

输出完成 (耗时 0 秒) - 正常终止

1、String类的构造器

(1)String():创建一个包含0个字符串序列的String对象(并不是返回null)
(2)String(byte[] bytes,Charset charset):使用指定的字符集将指定的byte[]数组解码成一个新的String对象。
(3)String(byte[] bytes,int offset,int length):使用指定的字符集将byte[]数组从offset开始、长度为length的子数组解码成一个新的String对象。
(4)String(byte[] bytes,String charsetName):使用指定的字符集将指定的byte[]数组解码成一个新的String对象。
(5)String(char[] value,int offset,int count):将指定字符数从offset开始、长度为count的字符元素连缀成字符串。
(6)String(String original):将字符串直接量创建一个字符串对象。也就是说新创建的String对象是该参数字符串的副本。
(7)String(StringBuffer buffer):根据StringBuffer对象创建对应的String对象。
(8)String(StringBuilder builder):根据StringBuilder对象创建对应的String对象。

2、操作字符串对象的方法

自行参看Java的API文档

四、Math类

Math类提供了大量的静态方法和两个类变量PI、E
方法参看API文档:


public class MathTest
{
	public static void main(String[] args)
	{
		/*---------下面是三角运算---------*/
		// 将弧度转换角度
		System.out.println("Math.toDegrees(1.57):"
			+ Math.toDegrees(1.57));
		// 将角度转换为弧度
		System.out.println("Math.toRadians(90):"
			+ Math.toRadians(90));
		// 计算反余弦,返回的角度范围在 0.0 到 pi 之间。
		System.out.println("Math.acos(1.2):" + Math.acos(1.2));
		// 计算反正弦;返回的角度范围在 -pi/2 到 pi/2 之间。
		System.out.println("Math.asin(0.8):" + Math.asin(0.8));
		// 计算反正切;返回的角度范围在 -pi/2 到 pi/2 之间。
		System.out.println("Math.atan(2.3):" + Math.atan(2.3));
		// 计算三角余弦。
		System.out.println("Math.cos(1.57):" + Math.cos(1.57));
		// 计算值的双曲余弦。
		System.out.println("Math.cosh(1.2 ):" + Math.cosh(1.2 ));
		// 计算正弦
		System.out.println("Math.sin(1.57 ):" + Math.sin(1.57 ));
		// 计算双曲正弦
		System.out.println("Math.sinh(1.2 ):" + Math.sinh(1.2 ));
		// 计算三角正切
		System.out.println("Math.tan(0.8 ):" + Math.tan(0.8 ));
		// 计算双曲正切
		System.out.println("Math.tanh(2.1 ):" + Math.tanh(2.1));
		// 将矩形坐标 (x, y) 转换成极坐标 (r, thet));
		System.out.println("Math.atan2(0.1, 0.2):" + Math.atan2(0.1, 0.2));
		/*---------下面是取整运算---------*/
		// 取整,返回小于目标数的最大整数。
		System.out.println("Math.floor(-1.2 ):" + Math.floor(-1.2 ));
		// 取整,返回大于目标数的最小整数。
		System.out.println("Math.ceil(1.2):" + Math.ceil(1.2));
		// 四舍五入取整
		System.out.println("Math.round(2.3 ):" + Math.round(2.3 ));
		/*---------下面是乘方、开方、指数运算---------*/
		// 计算平方根。
		System.out.println("Math.sqrt(2.3 ):" + Math.sqrt(2.3 ));
		// 计算立方根。
		System.out.println("Math.cbrt(9):" + Math.cbrt(9));
		// 返回欧拉数 e 的n次幂。
		System.out.println("Math.exp(2):" + Math.exp(2));
		// 返回 sqrt(x2 +y2)
		System.out.println("Math.hypot(4, 4):" + Math.hypot(4, 4));
		// 按照 IEEE 754 标准的规定,对两个参数进行余数运算。
		System.out.println("Math.IEEEremainder(5, 2):"
			+ Math.IEEEremainder(5, 2));
		// 计算乘方
		System.out.println("Math.pow(3, 2):" + Math.pow(3, 2));
		// 计算自然对数
		System.out.println("Math.log(12):" + Math.log(12));
		// 计算底数为 10 的对数。
		System.out.println("Math.log10(9):" + Math.log10(9));
		// 返回参数与 1 之和的自然对数。
		System.out.println("Math.log1p(9):" + Math.log1p(9));
		/*---------下面是符号相关的运算---------*/
		// 计算绝对值。
		System.out.println("Math.abs(-4.5):" + Math.abs(-4.5));
		// 符号赋值,返回带有第二个浮点数符号的第一个浮点参数。
		System.out.println("Math.copySign(1.2, -1.0):"
			+ Math.copySign(1.2, -1.0));
		// 符号函数;如果参数为 0,则返回 0;如果参数大于 0,
		// 则返回 1.0;如果参数小于 0,则返回 -1.0。
		System.out.println("Math.signum(2.3):" + Math.signum(2.3));
		/*---------下面是大小相关的运算---------*/
		// 找出最大值
		System.out.println("Math.max(2.3, 4.5):" + Math.max(2.3, 4.5));
		// 计算最小值
		System.out.println("Math.min(1.2, 3.4):" + Math.min(1.2, 3.4));
		// 返回第一个参数和第二个参数之间与第一个参数相邻的浮点数。
		System.out.println("Math.nextAfter(1.2, 1.0):"
			+ Math.nextAfter(1.2, 1.0));
		// 返回比目标数略大的浮点数
		System.out.println("Math.nextUp(1.2 ):" + Math.nextUp(1.2 ));
		// 返回一个伪随机数,该值大于等于 0.0 且小于 1.0。
		System.out.println("Math.random():" + Math.random());
	}
}

五、ThreadLocalRandom和Random ——java.util.Random

Random类专门用于生成随机数,它有两个构造器:一个构造器使用默认种子(以当前时间);一个构造器由程序员显示传入一个long型的种子。
ThreadLocalRandom是Java 7新增的一个类,是Random的加强版。在并发访问的环境下,使用ThreadLocalRandom来替代Random可以减少多线程资源竞争,最终保证系统具有跟好的线程安全。
ThreadLocalRandom类的用法与Random类相似,它提供一个current()方法获取ThreadLocalRandom对象,获取该对象后调用各种nextXxx()方法来获取随机数。

Random类使用默认种子(以当前时间)

import java.util.Random;
public class RandomTest
{
	public static void main(String[] args)
	{
		var rand=new Random();

		//public void nextBytes?(byte[] bytes)
		//将产生的随机byte类型的数据放到指定的byte[]数组中
		var buffer=new byte[16];
		rand.nextBytes(buffer);
		for(var bit:buffer)
		{
			System.out.print(" "+bit);
		}
		System.out.println();
		
		//生成随机的boolean类型
		System.out.println("random.nextBoolean():"+rand.nextBoolean());
		//生成随机的0.0-1.0的double类型数据
		System.out.println(rand.nextDouble());
		//生成随机的0.0-1.0的float类型数据
		System.out.println(rand.nextFloat());
		//生成平均值是0.0,标准差是1的伪高斯数
		System.out.println(rand.nextGaussian());
		//生成一个int整数取值范围的整数
		System.out.println(rand.nextInt());
		//生成一个0~25之间的伪随机数
		System.out.println(rand.nextInt(26));

	}
}

Random使用指定种子

Random使用一个48位的种子,如果这个类的两个实例是用同一个种子创建的,对它们以同样的顺序调用方法,则它们会产生相同的数字序列。
下面做一个实验,可以看到两个Random对象的种子相同时,它们会产生相同的数字序列。当使用默认的种子构造器创建Random对象时,它们属于同一种子。

import java.util.Random;
public class SeedTest
{
	public static void main(String[] args)
	{
		//第一个种子为50的对象
		var r1=new Random(50);
		System.out.println(r1.nextBoolean());
		System.out.println(r1.nextInt());
		System.out.println(r1.nextGaussian());
		System.out.println("------------------");
		//第二个种子为50的对象
		var r2=new Random(50);
		System.out.println(r2.nextBoolean());
		System.out.println(r2.nextInt());
		System.out.println(r2.nextGaussian());
		System.out.println("------------------");
		//第三个种子为100的对象
		var r3=new Random(100);
		System.out.println(r3.nextBoolean());
		System.out.println(r3.nextInt());
		System.out.println(r3.nextGaussian());
		System.out.println("------------------");
	}
}
---------- 运行Java捕获输出窗 ----------
true
-1727040520
1.4344888752894227
------------------
true
-1727040520
1.4344888752894227
------------------
true
-1139614796
-1.0565816491790574
------------------

输出完成 (耗时 0 秒) - 正常终止

可以看出Random产生的数字并不是真正的随机的,而是一种伪随机。为了避免两个Random对象产生相同的数字序列,通常推荐使用当前时间作为Random对象的种子。

Random rand=new Random(System.currentTimeMillus());//当然不写默认也是使用时间戳作为种子

在多线程下,ThreadLocalRandom的方方式与random基本相同。

只是生成对象时,使用current()方法

ThreadLocalRandom rand=ThreadLocalRandom.current();
//生成4-20之间的伪随机数
int val1=random.nextInt(4,20);
//生成2-10的伪随机浮点数
int val2=rand.nextDouble(2.0,10.0);

六、BigDcimal类——java.math.BigDecimal

float、double类型的数据进行运算时,容易造成精度损失:

public class DoubleTest
{
	public static void main(String args[])
	{
		System.out.println("0.05 + 0.01 = " + (0.05 + 0.01));
		System.out.println("1.0 - 0.42 = " + (1.0 - 0.42));
		System.out.println("4.015 * 100 = " + (4.015 * 100));
		System.out.println("123.3 / 100 = " + (123.3 / 100));
	}
}
---------- 运行Java捕获输出窗 ----------
0.05 + 0.01 = 0.060000000000000005
1.0 - 0.42 = 0.5800000000000001
4.015 * 100 = 401.49999999999994
123.3 / 100 = 1.2329999999999999

输出完成 (耗时 0 秒) - 正常终止

上面就表明,Java的double类型进行算数运算时,会发生精度损失。
为了精确表示、计算浮点数,Java提供了BigDecimal类,该类大量的构造器用于创建BigDecimal对象,包括把所有基本数据类型转换为一个BigDecimal对象,也包括利用数字字符串,数字字符数组来创建BigDecimal对象。
BigDecimal的两个构造器
BigDecimal(double var)——不推荐使用的构造器,因为其具有一定的不可预知性。例如0.1无法精确地表示为一个浮点数,所以传入BigDeciaml(0.1)构造器的值不会正好等于0.1(虽然表面上是)。
BigDecimal(String var)——结果是预知的,写入new BigDecimal("0.1")将创建一个BigDecimal,它正好等于0.1.因此通常推荐使用基于String的构造器。
如果必须使用double数作为BigDecimal构造器的参数,不要直接将该double浮点数作为参数创建BigDecimal对象,而是应该通过BigDecimal.valueOf(double value)静态方法创建BigDecimal对象。
BigDecimal类提供大量的pow()、add()、subtrat()、multiply()、divide()等方法对浮点数进行常规的算术运算。

import java.math.BigDecimal;
class BigDecimalTest 
{
	public static void main(String[] args) 
	{
		var f1=new BigDecimal("0.05");
		var f2=BigDecimal.valueOf(0.01);
		var f3=new BigDecimal(0.05);
		System.out.println("使用String作为BigDecimal构造器的参数:");
		System.out.println("0.05+0.01="+f1.add(f2));
		System.out.println("0.05-0.01="+f1.subtract(f2));
		System.out.println("0.05*0.01="+f1.multiply(f2));
		System.out.println("0.05/0.01="+f1.divide(f2));
		System.out.println("使用double作为BigDecimal构造器的参数:");
		System.out.println("0.05+0.01="+f3.add(f2));
		System.out.println("0.05-0.01="+f3.subtract(f2));
		System.out.println("0.05*0.01="+f3.multiply(f2));
		System.out.println("0.05/0.01="+f3.divide(f2));
	}
}
---------- 运行Java捕获输出窗 ----------
使用String作为BigDecimal构造器的参数:
0.05+0.01=0.06
0.05-0.01=0.04
0.05*0.01=0.0005
0.05/0.01=5
使用double作为BigDecimal构造器的参数:
0.05+0.01=0.06000000000000000277555756156289135105907917022705078125
0.05-0.01=0.04000000000000000277555756156289135105907917022705078125
0.05*0.01=0.0005000000000000000277555756156289135105907917022705078125
0.05/0.01=5.000000000000000277555756156289135105907917022705078125

输出完成 (耗时 0 秒) - 正常终止

从上面的运算结果来看,使用String作为参数的创建的BigDecimal对象比直接使用double数字更具可靠。
如果程序中要求对double类型的浮点数进行加减乘除等基本运算,则需要先将double类型的数值包装成BigDecimal对象,调用BigDeciaml的方法执行运算后再将结果转换成double类型,这个过程比较繁琐,所以以BigDecimal为基础定义了一个Arith工具类:
该工具类的代码:


import java.math.*;
public class Arith
{
	// 默认除法运算精度
	private static final int DEF_DIV_SCALE = 10;
	// 构造器私有,让这个类不能实例化
	private Arith()	{}
	// 提供精确的加法运算。
	public static double add(double v1, double v2)
	{
		var b1 = BigDecimal.valueOf(v1);
		var b2 = BigDecimal.valueOf(v2);
		return b1.add(b2).doubleValue();
	}
	// 提供精确的减法运算。
	public static double sub(double v1, double v2)
	{
		var b1 = BigDecimal.valueOf(v1);
		var b2 = BigDecimal.valueOf(v2);
		return b1.subtract(b2).doubleValue();
	}
	// 提供精确的乘法运算。
	public static double mul(double v1, double v2)
	{
		var b1 = BigDecimal.valueOf(v1);
		var b2 = BigDecimal.valueOf(v2);
		return b1.multiply(b2).doubleValue();
	}
	// 提供(相对)精确的除法运算,当发生除不尽的情况时.
	// 精确到小数点以后10位的数字四舍五入。
	public static double div(double v1, double v2)
	{
		var b1 = BigDecimal.valueOf(v1);
		var b2 = BigDecimal.valueOf(v2);
		return b1.divide(b2, DEF_DIV_SCALE,
			RoundingMode.HALF_UP).doubleValue();
	}
	public static void main(String[] args)
	{
		System.out.println("0.05 + 0.01 = "
			+ Arith.add(0.05, 0.01));
		System.out.println("1.0 - 0.42 = "
			+ Arith.sub(1.0, 0.42));
		System.out.println("4.015 * 100 = "
			+ Arith.mul(4.015, 100));
		System.out.println("123.3 / 100 = "
			+ Arith.div(123.3, 100));
	}
}
---------- 运行Java捕获输出窗 ----------
0.05 + 0.01 = 0.06
1.0 - 0.42 = 0.58
4.015 * 100 = 401.5
123.3 / 100 = 1.233

输出完成 (耗时 0 秒) - 正常终止
原文地址:https://www.cnblogs.com/weststar/p/12463304.html