对象流
- 将单个对象保存到文件中
- 将集合保存到文件中
✏️student对象
public class Student implements Serializable {
private int sid;
private String name;
private String pass;
private int age;
private double score;
public Student() {
}
public Student(int sid, String name, String pass, int age, double score) {
this.sid = sid;
this.name = name;
this.pass = pass;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", name='" + name + '\'' +
", pass='" + pass + '\'' +
", age=" + age +
", score=" + score +
'}';
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
将单个对象保存到文件中
✏️SaveObject.java
public class SaveObject {
public static void main(String[] args) {
String path = "stu.txt";
File file = null;
Student s1 = new Student(9527, "zhouxingxing", "999999", 20, 100);
file = saveObject(path,s1);
readObject(file);
}
public static File saveObject(String path,Object object){
File file = new File(path);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(object);
System.out.println("保存成功");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(oos != null){
oos.close();
oos = null;
}
if(fos != null){
fos.close();
fos = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
public static void readObject(File file){
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(file);
ois = new ObjectInputStream(fis);
Object o = ois.readObject();
Student stu = (Student) o;
System.out.println(stu);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(ois != null){
ois.close();
ois = null;
}
if(fis != null){
fis.close();
fis = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
序列化集合
📖当我们想在文件中保存多个对象的时候,可以把多个对象存储到一个集合中对集合进行序列化和反序列化。
✏️SaveObjects.java
public class SaveObjects {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.定义一个存储Person对象的ArrayList集合
List<Student> students = new ArrayList<>();
//2.往ArrayList集合中存储Person对象
students.add(new Student(9527, "zhouxingxing1", "999999", 20, 100));
students.add(new Student(9528, "zhouxingxing2", "999998", 21, 101));
students.add(new Student(9529, "zhouxingxing3", "999997", 22, 102));
File file = new File("stu3.txt");
//将students集合序列化到文件中
save(file, students);
//从文件中反序列化输出Student集合对象
read(file);
}
public static void save(File file,List<Student> list) throws IOException, ClassNotFoundException {
//3.创建一个序列化流ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
// 4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
oos.writeObject(list);
//9.释放资源
oos.close();
}
public static void read(File file) throws IOException, ClassNotFoundException {
//5.创建一个反序列化流ObjectInputStream对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
//6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
Object o = ois.readObject();
//7.把Object类型的集合转换为ArrayList类型
ArrayList<Student> list2 = (ArrayList<Student>) o;
//8.遍历ArrayList集合
for (Student student : list2) {
System.out.println(student);
}
//9.释放资源
ois.close();
}
}
RandomAccessFile
📖:RandomAccessFile是Java提供的可以对文件内容进行访问的类,既可以对文件内容进行读取操作,也可以写入新的内容,并且RandomAccessFile支持随机访问文件,即访问文件内容的任意位置, 常应用于断点续传。
✏️:构造函数
- public RandomAcessFile(File file,String mode); //用String 字符串来指定文件名
- public RandomAccessFile(String name,String mode);//用File对象来指定
✏️:mode值
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
"rwd" 模式可用于减少执行的 I/O 操作数量。使用 "rwd" 仅要求更新要写入存储的文件的内容;使用 "rws" 要求更新要写入的文件内容及其元数据,这通常要求至少一个以上的低级别 I/O 操作。
TestRandomAccessFile.java
public class TestRandomAccessFile {
public static void main(String[] args) {
File f = new File("a.txt");
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(f, "rw");
raf.write("hello".getBytes());
raf.writeInt(1000);
raf.write(200);
raf.writeBoolean(false);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("===============");
RandomAccessFile raf2 = null;
try {
raf2 = new RandomAccessFile("a.txt", "r");
byte[] b = new byte[5];
//raf2.read(b);
raf2.skipBytes(4);
System.out.println(new String(b));
int num = raf2.readInt();
System.out.println(num);
int num2 = raf2.read();
System.out.println(num2);
boolean flag = raf2.readBoolean();
System.out.println(flag);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
补充学习
重写equals和hashCode方法
重写equals
✏️应用实例: 判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。
public class EqualsExercise01 {
public static void main(String[] args) {
Person person1 = new Person("jack", 10, '男');
Person person2 = new Person("jack", 10, '男');
System.out.println(person1.equals(person2));
//如果不重写equals方法则两个即使属性都相同但不是同一个对象,返回结果为FALSE
}
}
//判断两个 Person 对象的内容是否相等,
//如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false
class Person{ //extends Object
private String name;
private int age;
private char gender;
//重写 Object 的 equals 方法
public boolean equals(Object obj) {
//判断如果比较的两个对象是同一个对象,则直接返回 true
if(this == obj) {
return true;
}
//类型判断
if(obj instanceof Person) {//是 Person,我们才比较
//进行 向下转型, 因为我需要得到 obj 的 各个属性
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
//如果不是 Person ,则直接返回 false
return false;
}
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
}
public class EqualsExercise02 {
public static void main(String[] args) {
Person_ p1 = new Person_();
p1.name = "jack";
Person_ p2 = new Person_();
p2.name = "jack";
System.out.println(p1==p2); //False
System.out.println(p1.name .equals( p2.name));//T
System.out.println(p1.equals(p2));//False
String s1 = new String("asdf");
String s2 = new String("asdf");
System.out.println(s1.equals(s2));//T
System.out.println(s1==s2); //F
}
}
class Person_{//类
public String name;
}
hashCode方法
📖返回该对象的哈希码值。支持此方法是为了提高哈希表(例如java. util.Hashtable提供的哈希表〉的性能。实际上。由 Object类定义的hashCode方法确实会针对不同的对象返回不同的整数。返回:此对象的一个哈希码值。
-
提高具有哈希结构的容器的效率!
-
两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
-
两个引用,如果指向的是不同对象,则哈希值是不一样的
-
哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址。
public class HashCode_ {
public static void main(String[] args) {
AA aa = new AA();
AA aa2 = new AA();
AA aa3 = aa;
System.out.println("aa.hashCode()=" + aa.hashCode());//aa.hashCode()=356573597
System.out.println("aa2.hashCode()=" + aa2.hashCode());//aa2.hashCode()=1735600054
System.out.println("aa3.hashCode()=" + aa3.hashCode());//aa3.hashCode()=356573597
}
}
class AA {}
重写 equals 方法 和 hashCode在集合中的应用(LinkedHashSet)
public class LinkedHashSetExercise {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add(new Car("奥拓", 1000));//OK
linkedHashSet.add(new Car("奥迪", 300000));//OK
linkedHashSet.add(new Car("法拉利", 10000000));//OK
linkedHashSet.add(new Car("奥迪", 300000));//加入不了
linkedHashSet.add(new Car("保时捷", 70000000));//OK
linkedHashSet.add(new Car("奥迪", 300000));//加入不了
System.out.println("linkedHashSet=" + linkedHashSet);
}
}
/**
* Car 类(属性:name,price), 如果 name 和 price 一样,
* 则认为是相同元素,就不能添加。
*/
class Car {
private String name;
private double price;
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "\nCar{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
//重写 equals 方法 和 hashCode
//当 name 和 price 相同时, 就返回相同的 hashCode 值, equals 返回 t
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return Double.compare(car.price, price) == 0 &&
Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(name, price);
}
}
面试题
- 重写equals方法的原因
(1)Object类中equals方法比较的是两个对象的引用地址,只有对象的引用地址指向同一个地址时,才认为这两个地址是相等的,否则这两个对象就不想等。
(2)如果有两个对象,他们的属性是相同的,但是地址不同,这样使用equals()比较得出的结果是不相等的,而我们需要的是这两个对象相等,因此默认的equals()方法是不符合我们的要求的,这个时候我们就需要对equals()方法进行重写以满足我们的预期结果。
(3)在java的集合框架中需要用到equals()方法进行查找对象,如果集合中存放的是自定义类型,并且没有重写equals()方法,则会调用Object父类中的equals()方法按照地址比较,往往会出现错误的结果,此时我们应该根据业务需求重写equals()方法。
- 重写hashCode方法的原因
(1)hashCode()方法用于散列数据的快速存储,HashSet/HashMap/Hashtable类存储数据时都是根据存储对象的hashcode值来进行分类存储的,一般先根据hashcode值在集合中进行分类,在根据equals()方法判断对象是否相同。
(2)HashMap对象是根据其Key的hashCode来获取对应的Value。
(3)生成一个好的hashCode值能提高HashSet查找的性能,差的hashCode值不但不能提高性能,甚至可能造成错误。比如hashCode方法中返回常量,会让,HashSet的查找效率退化为List集合的查找效率;hashCode方法中返回随机数,会让查找结果变的不可预测。
(4)好的hashCode生成方式是让对象中的关键属性与质数相乘,并将积相加获取。
同时由于重写了equals,应该对hashcode方法进行重写原因:
(1)为了维护hashCode()方法的equals协定,该协定指出:如果根据 equals()方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode方法都必须生成相同的整数结果;而两个hashCode()返回的结果相等,两个对象的equals()方法不一定相等。
(2)HashMap对象是根据其Key的hashCode来获取对应的Value。
(3)在重写父类的equals()方法时,也重写hashcode()方法,使相等的两个对象获取的HashCode值也相等,这样当此对象做Map类中的Key时,两个equals为true的对象其获取的value都是同一个,比较符合实际。
- ❤️为什么要重写equals和hashCode?
因为这两个方法都跟对象的比较有关,所以如果在程序中要做对象比较,那大概率要重写这两个方法了。因为equals默认的比较逻辑就是对象的地址进行比较,两个对象内存地址肯定不同,所以无论如何两个对象通过eqals比较肯定返回false。但在实际编程中,我们经常会遇到去重,或者将对象放到有序集合中,或者将对象存入无重复的集合中,这时如果没有重写equals和hashCode,则无法达到需求。
举个例子:系统中同时存在两个对象,对象A和对象B,其姓名和身份证号一模一样。此时,在系统内存中是两个对象,但其内容一致分明是一个人同时产生了两条重复信息。如果使用默认的equals方法比较,则这两个对象永远不相等,永远不能比出来是一条相同的重复信息。所以,要重写equals和hashCode方法来达到以上需求效果。