java学习第14天

java学习第14天

序列化

java在运行时,如果需要保存对象的状态(下次程序在运行时,也能还原当前对象的状态)就需要使用序列化操作。本质是把对象保存为一个文件存到磁盘上,下次运行时从磁盘上读取文件,恢复对象(反序列化)。

举例:Student对象,在运行时给姓名赋值为张三,年龄赋值为20岁,这个对象在程序运行结束就消失了,下次程序运行时又要重新创建对象并赋值。要想下一次运行时这个对象还存在,这种情况就需要使用序列化。

网络程序,如果想把一个对象从一台机器(虚拟机)发送到另外一台机器(虚拟机),这种情况也需要把对象序列化为二进制的内容,如何再通过网络发送,对象收到二进制内容,再反序列化为对象。

ObjectOutputStream(序列化)

把运行的对象保存为文件。

要被序列化的对象的类,要实现Serializable接口

  • Student类,实现节后Serializable

    public class Student implements Serializable {
        private String name;
        private int age;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override
        public String toString() {
            return "学生:{" + "姓名是" + name + ", 年龄为" + age + '}';
        }
    
  • SerializableDemo

    利用ObjectOutputStream的writeObject方法做序列化

    public static void main(String[] args) throws Exception {
            ObjectOutputStream oot = new ObjectOutputStream(new FileOutputStream("D:\\idea\\javaseprojects\\java_Hqyj_Day14\\src\\abc.txt"));
            Student student = new Student("张三",23);
            oot.writeObject(student);
            oot.close();
        }
    

ObjectInputStream(反序列化)

调用readObject()反序列化,该方法返回的类型是Object,所以要强制转换

读取的序列化文件一定是正常序列化的文件,并且类型要相同。

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\idea\\javaseprojects\\java_Hqyj_Day14\\src\\abc.txt"));
        //读取序列化的文件,还原为原来的对象(对象的属性都还在)
        Student o = (Student) ois.readObject();
        System.out.println(o);
        ois.close();

复习

构造方法在什么时候使用?有参和无参的区别在哪里?

构造方法在创建对象(实例化)的时候会被调用(也就是使用new Xxx() ),还有一种情况就是子类创建对象时会先调用父类的构造方法,然后在调用子类的构造方法。在子类的构造方法中没有明确的调用父类的构造方法,自动调用父类的无参构造方法。

new的关键字使用的时候,类名后面的小括号里面有几个参数,就会调用对应参数数量的构造方法。

例如:new Student(),调用无参构造方法;

new Student(String name),调用一个参数的构造方法。

new Student(String name,int age),调用两个参数的构造方法。

用哪一个构造方法是程序员自己决定的。调用有参数的构造方法的目的是创建对象的同时给属性赋值。

  • Emp

    package com.Hqyj.ObjectOutputStream;
    
    public class Emp  {
        protected int empNum;//员工编号
        protected String name;//员工姓名
        protected int age;//员工年龄
    
        public Emp() {
            System.out.println("父类的无参构造方法被执行");
        }
    
        public Emp(int empNum) {
            this.empNum = empNum;
        }
    
        public Emp(int emp, String name, int age) {
            this.empNum = emp;
            this.name = name;
            this.age = age;
            System.out.println("父类的有参构造方法被执行");
        }
    
        public int getEmpNum() {
            return empNum;
        }
    
        public void setEmpNum(int empNum) {
            this.empNum = empNum;
        }
    
        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;
        }
    
        @Override
        public String toString() {
            return "Emp{" +
                    "emp=" + empNum +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
  • EmpHqyj

    package com.Hqyj.ObjectOutputStream;
    
    public class EmpHqyj extends Emp{
    
        public EmpHqyj() {
            System.out.println("子类的无参构造方法被执行");
        }
    
    
        public EmpHqyj(int emp, String name, int age) {
            //super(emp, name, age);//调用父类的构造方法
            this.empNum = emp;
            this.name = name;
            this.age = age;
            System.out.println("子类的有参构造方法");
        }
    }
    
    
  • EmpTest

    package com.Hqyj.ObjectOutputStream;
    
    public class EmpTest {
        public static void main(String[] args) {
            //用无参的构造方法创建对象,属性都是默认值
            Emp emp = new Emp();
            //要给属性赋值,使用setXxx()方法
            emp.setEmpNum(1001);
            emp.setName("张三");
            emp.setAge(25);
            System.out.println(emp);
    
            //用有参(三个)构造方法创建对象,它会在创建的同时给属性赋值。
            Emp emp1 = new Emp(1002,"李四",26);
            System.out.println(emp1);
    
            //用一个参数的构造方法创建对象。
            Emp emp2 = new Emp(1003);
            System.out.println(emp2);
    
            /* 测试EmpHqyj*/
            System.out.println("=======EmpHqyj======");
            EmpHqyj empHqyj = new EmpHqyj();
            System.out.println(empHqyj);
    
            //子类的有参构造方法如果没有明确调用父类的构造犯法,会自动调用父类的无参构造方法
            EmpHqyj empHqyj1 = new EmpHqyj(1004,"王五",24);
            System.out.println(empHqyj1);
    
        }
    }
    
    
常量池中存的是一种对象吗?

常量池中存的也是对象,主要正对String类型,如果用字面量赋值的String变量,就会在常量池中创建一个对象,下一次再用相同的字符串给变量赋值的时候,就会共用常量池中的一个对象。

基本类型不会赋值给对象。

package com.Hqyj.ObjectOutputStream;

public class StringTest {
    public static void main(String[] args) {
        String str = "abc";
        //判断一个对象是否是某个类型
        System.out.println(str instanceof String);
        String str2 = "abc";
        System.out.println(str == str2);
        System.out.println(str.equals(str2));

        String str3 = new String("abc");
        System.out.println("str == str3?"+(str == str3));
        System.out.println("str.equals(str3)?"+ str.equals(str3));
    }
}
对于泛型List,如果进行引用,虚拟机不能自动垃圾回收吗?

运行参数设置,虚拟机最大和最小都设为20M

在这里插入图片描述

测试对象不被回收,导致内存溢出

package com.Hqyj.ObjectOutputStream;

import java.util.ArrayList;
import java.util.List;

public class GcTest {
    public static void main(String[] args) {
        List<Emp> emps = new ArrayList<>();
        while (true){
            // 因为emp对象被list引用,所以emp不能被垃圾回收。
            //当内层1超过上限就会报OutofMemaryError
            //emps.add(new Emp());

            //emp对象知赋给局部变量,这个变量下一次循环就消失了(不在被引用)所以能被回收
            //所以内存不容易超过极限
            Emp emp2 = new Emp();
            System.out.println(System.currentTimeMillis());
        }
    }
}

关于方法的调用

方法用来执行某一个特定功能的一段代码,方法可以重复使用。使main里面的代码尽量简洁。

方法也是类的一种组成部分

方法的定义:例如

public int fun1(int i, int j){

//方法体,方法内的代码

return 0;

}

方法的调用:

int m = obj.fun1(5, 20);

package com.Hqyj.ObjectOutputStream;

public class MethodDemo {
    public static void main(String[] args) {
        MethodDemo methodDemo = new MethodDemo();
        int sum = methodDemo.sum(25,63);
        System.out.println(sum);
    }

    public int sum(int a, int b) {
        return a + b;
    }
}

  • 重写和重载的区别

  • 可变长的参数

    定义方法的时候,参数类型后面跟上三个点…,这个参数就叫可变长参数,调用该方法时可以传多个参数。

    可变参数只能是最后一个参数。

    int void fun2(String …str);

  • 方法里面的变量都是局部变量,方法里面的引用类型的变量只保存对象的地址,实际对象存在堆里面的。

    方法被调用时在栈里面会有一段内存(方法栈帧)保存局部变量,方法调用时入站,方法调用结束出栈,出栈后释放栈帧的内存,局部变量不可再访问。

    如果方法里面调用另一个方法(如A调用B),A方法暂停运行,先执行被调用的B方法,B方法入栈,等被B方法执行完成后,A方法在继续执行。

try语句块中出现return语句,finally还会执行吗?

会执行,即便出现Error,finally也会执行,

抽象 abstract

抽象可以修饰类,方法。

抽象方法,就是这个方法没有方法体(没有代码),抽象方法的目的是为了统一规范方法的定义,具体的实现交给子类去实现,一般来说抽象方法在每个子类中实现都不一样。例如:形状类计算面积的方法,父类中就没有办法去实现这个方法,因为不同的形状面积的计算公式不一样,所以只能让长方形,三角形,梯形等这些子类自己去实现面积计算方法。如果不统一方法名,那每个子类可能会写出不同的方法名,不方便向上造型之后的统一调用。

一个类只要有一个方法是抽象方法,这个类就必须定义为抽象类。抽象类中可以只有抽象方法,也可以有抽象方法和具体方法,也可以只有具体方法,抽象类不能创建对象。

抽象类可以有构造方法,子类在创建对象时,抽象类的构造方法也会被调用。

所有的方法都是抽象类,这个抽象类就可以定义为接口(interface),interface的子类就用implements关键字实现接口,可以实现多个接口,接口可以相互继承。

接口里面的方法不写访问修饰符,默认就是Public,如果属性不写修饰符,默认就是public static final(常量)

  • 抽象类Shape
public abstract class Shape {
    public abstract void area(int a, int b);
}

  • 子类Rectangle
public class Rectangle extends Shape {
    @Override
    public void area(int a, int b) {
        System.out.println(a * b);//长方形的面积等于长乘宽
    }
}
  • 子类Triangle
public class Tritangle extends Shape {
    @Override
    public void area(int a, int b) {
        System.out.println(1.0 / 2 * a * b);//三角形的面积底乘高乘1/2.
    }
}

  • 测试代码
public class ShapeTest {
    public static void main(String[] args) {
        Shape shapeRect = new Rectangle();//多态的向上造型
        System.out.println("长方形的面积");
        shapeRect.area(6,4);
        Shape shapeTrit = new Tritangle();
        System.out.println("三角形的面积");
        shapeTrit.area(4,8);
    }
}

Static静态

static可以修饰类,方法,属性,代码块

static是属于类的,不属于对象:

  1. static在类加载的时候执行(静态代码块最先执行),不是在创建对象时执行。
  2. static是所有对象共享的,不属于对象独有的。
  3. static的访问可以直接用类名访问。static的方法只能访问static的属性或方法

序列化时不会序列化类的静态成员

  • staticDemo

    package com.Hqyj.ObjectOutputStream;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class StaticDemo {
        public static int count = 0;
        private int i;
        private String str;
    
        public static void showTime(){
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println(sdf.format(date));
        }
    
        public StaticDemo() {
            System.out.println("static静态代码块的无参构造器");
        }
    
        static {
            System.out.println("static静态代码块");
        }
    
        public int getI() {
            return i;
        }
    
        public void setI(int i) {
            this.i = i;
        }
    
        public String getStr() {
            return str;
        }
    
        public void setStr(String str) {
            this.str = str;
        }
        
         public static int getCount() {
            return count;
        }
    
        @Override
        public String toString() {
            return "StaticDemo{" +
                    "i=" + i +
                    ", str='" + str + '\'' +
                    '}';
        }
    }
    
    
  • StaticDemo

    package com.Hqyj.ObjectOutputStream;
    
    public class StaticTest {
        public static void main(String[] args) {
            //直接用类名访问静态成员变量
            System.out.println("count"+ StaticDemo.count);
            //直接用类名调用静态方法
            StaticDemo.showTime();
            StaticDemo sD = new StaticDemo();
            StaticDemo sD1 = new StaticDemo();
    
            StaticDemo.count++;
            System.out.println(sD.getCount());
            System.out.println(sD1.getCount());
        }
    }
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值