常用类
Object类
Object
类位于java.lang
包中,java.lang
包包含着Java最基础和核心的类,在编译时会自动导入;
Object类是所有Java类的根基类,如果在类的声明中未使用extends关键字指明其基类,则默认基类为Object类。
public class Person{
}
public class Person extends Object{
}
在Object类中提供了一些常用的方法
toString方法
Object
类中定义有public String toString()
方法,其返回值是 String 类型,描述当前对象的有关信息。
在进行String与其它类型数据的连接操作时(如:System.out.println(“info”+person)),将自动调用该对象类的 toString()方法。
可以根据需要在用户自定义类型中重写toString()方法。
equals方法
Object类中定义有: public boolean equals(Object obj) 方法提供定义对象是否 “相等” 的逻
辑。
Object 的 equals 方法 定义为: x.equals (y) 当 x 和 y是同一个对象的应用时返回 true 否则返
回 false
JDK提供的一些类,如String,Date等,重写了Object的equals方法,调用这些类的equals方法,x.equals (y) ,当x和y所引用的对象是同一类对象且属性内容相等时(并不一定是相同对象),返回 true 否则返回 false。
可以根据需要在用户自定义类型中重写equals方法。
hashCode方法
在Java的Object类中还有一个方法 public native int hashCode();
该方法返回一个int类型的数值,并且是本地方法。hashcode
本身代表对象的地址,说的是对象在hash表中的位置,物理地址说的对象存放在内存中的地址,java中通过对象的内部地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode,这基本来说就是对象的唯一标识了。既然是唯一标识它主要是为了查找以及比较的快捷性。
对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。
当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(该集合中不允许重复的元素存在)
如果直接调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,从而提高了效率。
我们会重写hashCode方法,使其和对象的属性值相关,不同对象,相同的属性得到的hash值相同; 并且不同对象,不同的属性也可能得到相同的hash值。反之,不同的hash值,一定是不同的对象,不同的属性。
clone方法
java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。
Student s1=new Student();
s1.setAge(17);
s1.setName("Tom");
Student s2=s1;
System.out.println(s1==s2);//true
此时只存在一个Student对象!!!
如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态,而互不影响,就需要用到java中对象的复制,如原生的 clone() 方法。
Object对象有个clone()方法,实现了对象中各个属性的复制,但它的可见范围是protected的,所以实体类使用克隆的前提是:实现Cloneable接口,这是一个标记接口,自身没有方法;覆盖clone()方法,可见性提升为public。
如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。
Student.java
实体类
package com.uek.pojo;
import java.util.Objects;
/**
* @version 0.0.1
* @auther ycb
*/
public class Student implements Cloneable{
private int age;
private String name;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
测试
package com.uek.obj;
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student(17, "Tom");// 通过clone方法来复制对象
Student s2 = (Student) s1.clone();
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
System.out.println(s1);
System.out.println(s2);
}
}
浅拷贝和深拷贝
浅拷贝:被复制对象的所有值属性都含有与原来对象的相同,而所有的对象引用属性仍然指向原来的对象。
深拷贝:在浅拷贝的基础上,所有引用其他对象的变量也进行了clone,并指向被复制过的新对象。
也就是说,一个默认的clone()方法实现机制,仍然是赋值。如果一个被复制的属性都是基本类型,那么只需要实现当前类的cloneable机制就可以了,此为浅拷贝。
如果被复制对象的属性包含其他实体类对象引用,那么这些实体类对象都需要实现cloneable
接口并覆盖clone()
方法。
包装类
概述
JAVA并不是纯面向对象的语言。Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的。但是我们在实际使用中经常需要将基本数据转化成对象,便于操作。比如:集合的操作中。例如使用Map对象要操作put()方法时,需要传入的参数是对象而不是基本数据类型。为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为 包装类(Wrapper Class) 。
包装类均位于 java.lang 包,包装类和基本数据类型的对应关系如下表所示
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
在这八个类名中,除了Integer和Character类以后,其它六个类的类名和基本数据类型一直,只是类名的第一个字母大写即可。
包装类的用途
对于包装类说,这些类的用途主要包含两种:
- 作为和基本数据类型对应的类类型存在,方便涉及到对象的操作。
- 包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本类型数据、包装类对象、字符串之间提供转化!)。
所有的包装类(Wrapper Class)都有共同的方法,他们是:
-
基本类型转化成Integer对象
-
Integer int1 = new Integer(10); //在jdk9中已经废弃了 Integer int2 = Integer.valueOf(20);
-
-
Integer对象转化成int
-
int a = int1.intValue();
-
-
字符串转化成Integer对象
-
Integer int3 = Integer.parseInt("334"); Integer int4 = new Integer("999");
-
-
Integer对象转化成字符串
-
String str1 = int3.toString();
-
-
一些常见int类型相关的常量
-
System.out.println("int能表示的最大整数:"+Integer.MAX_VALUE);
-
自动装箱和拆箱
就是将基本类型和包装类进行自动的互相转换。JDK5.0后,将自动装箱/拆箱引入java中。
自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中。
自动拆箱的过程:每当需要一个值时,被装箱对象中的值就被自动地提取出来,没必要再去调用intValue()和doubleValue()方法。
自动装箱与拆箱的功能事实上是编译器来帮您的忙,编译器在编译时期依您所编写的语法,决定是否进行装箱或拆箱动作。
Integer i = 100;
相当于编译器自动为您作以下的语法编译:
Integer i = new Integer(100);
所以自动装箱与拆箱的功能是所谓的 “编译器蜜糖”(Compiler Sugar) ,虽然使用这个功能很方便,但在程序运行阶段您得了解Java的语义。例如下面的程序是可以通过编译的:
Integer i = null;
int j = i;
这样的语法在编译时期是合法的,但是在运行时期会有错误,因为这种写法相当于:
Integer i = null;
int j = i.intValue();
null 表示 i 没有参考至任何的对象实体,它可以合法地指定给对象参考名称。由于实际上 i 并没有参考至任何的对象,所以也就不可能操作 intValue() 方法,这样上面的写法在运行时会出现NullPointerException
错误。
缓存
自动装箱拆箱时,对于-128-127之间的值,编译器仍然会把它当做基本类型处理。
Integer h = 100;
Integer u = 100;
Integer h2 = 200;
Integer u2 = 200;
if(h==u){
System.out.println("100等于");
}
if(h2==u2){
System.out.println("200等于");
}
时间处理相关类
作为与我们息息相关的日期,在开发中也是很常见的。在程序中,日期的存储也是个数字,是个长整型的数字。0代表1970年1月1日 00:00:00,而我们处于东八区,所以为1970年1月1日 08:00:0 ,往前推,负数表示之前的时间,往后算正数表示之后的时间。
而在日常中,我们表示日期是使用字符串表示,如 1970年1月1日 08:15 、 1970/01/01 08:15
1970-01-01 08:15 等等,因此就涉及到 如何将长整型数与字符串互转的问题,这也是学习的重点。
版本8之前常用日期类
在标准Java类库中包含一个 Date 类。它的对象表示一个特定的瞬间,精确到毫秒。
Date分配一个Date对象,并初始化此对象为当前的日期和时间精确到毫秒。
Date 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1月 1 日 00:00:00 GMT)以来的指定毫秒数。
类 | 常见方法 |
---|---|
System系统类 | currentTimeMillis(): 当前时间 |
Date日期类 | new Date() :当前时间 new Date(指定时间):指定时间 getTime(): 获取时间 setTime(指 |