IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> Java基础(四) -> 正文阅读

[开发工具]Java基础(四)

序列化和反序列化

序列化 Serialize

将java对象存储到硬盘上(也可以是用于网络传输的字节流)中;

反序列化 Deserialize

将硬盘上的数据重新恢复到内存中,变成Java对象。

参考代码

public class SerializeTest {
    public static void main(String[] args) {
        Student student = new Student("关灯吃面","秋名RG");
        try (
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student"));
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student"))
        ) {
            //序列化一个对象
            //如果序列多个对象,可以用List集合
            oos.writeObject(student);
            oos.flush();
            Object o = ois.readObject();
            System.out.println(o);
        } catch (FileNotFoundException fileNotFoundException) {
            fileNotFoundException.printStackTrace();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

class Student implements Serializable {

    private String hobby;
    //在整个对象被序列化时,transient修饰的字段不会被序列化。
    private transient String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public Student(String hobby, String name) {
        this.hobby = hobby;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "hobby='" + hobby + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

需要注意的点

被序列化和被反序列化的对象,需要继承Serializable接口,这个接口是标志接口,当Java虚拟机看到这个接口时,就会知道这家伙需要序列化,于是就生成一个序列化版本号。

序列化版本号的作用

在反序列化时,首先通过 类名+序列化版本号 辨别应该反序列化成哪个类,如果 类名+序列化版本号 验证通过,则正常反序列化。

继承Serializable接口是否就足够了?

如果只是继承Serializable接口,JVM会自动生成一个序列化版本号,在序列化时,则将对象信息连带版本号一起序列化,进入硬盘或者字节流中(用于网络传输)。缺点是,如果在序列化之后,我们修改了类的代码,重启JVM,JVM一看代码变化了会重新生成一个序列化版本号,那么之前我们序列化过的对象就无法反序列化回来了,会报错:

java.io.InvalidClassException: 
    daydayup.day002.Student; 
    local class incompatible: stream classdesc serialVersionUID = 21338476354139427, local class serialVersionUID = 8715101302771642032

怎么解决这个缺点?

解决方案是,我们可以手动添加一个序列化版本号,这样在修改代码后,这个类仍然是这个类,不会产生上面的异常。当然了,要是改的面目全非,数据也不会显示的。

intellij idea中可以这么做

配置完之后就会有提示,按下 Alt+Enter即可生成。

在这里插入图片描述

//类似这样的效果
private static final long serialVersionUID = 21338476354139427L;

IO读取properties

public class IOPropertiesTest {
    public static void main(String[] args) {
        try (FileReader fileReader = new FileReader("src/main/resources/document/test.properties")) {
            Properties properties = new Properties();
            properties.load(fileReader);

            String name = properties.getProperty("username");
            String pw = properties.getProperty("password");
            System.out.println(name);
            System.out.println(pw);
        } catch (FileNotFoundException fileNotFoundException) {
            fileNotFoundException.printStackTrace();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}

多线程

进程和线程

进程是一个应用程序,线程是进程中的执行单元。一个进程可以启动多个线程。

在Dos窗口执行’java HelloWorld’ 命令时,发生了什么?

先启动JVM,一个JVM就是一个进程。JVM启动一个线程调用main方法,同时再启动一个垃圾回收线程负责回收垃圾。也就是说,Java程序中至少也是两个线程并发。

调用线程最简单的三种方式

public class MultiThreadTest01 {

    public static void main(String[] args) {

        // 继承Thread,重写run方法
        MyThread01 mt1 = new MyThread01();
        //启动一个分支线程在JVM中开辟一个新的栈空间
        // start() 会在开辟空间之后就结束,像一个导火索
        // 与此同时
        mt1.start();


        //实现Runnable接口,利用线程包装
        Thread mt2 = new Thread(new MyThread02());
        //启动一个分支线程在JVM中开辟一个新的栈空间
        // start() 会在开辟空间之后就结束,像一个导火索
        // 与此同时
        mt2.start();

        //匿名内部类
        Thread mt3 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("m3:" + i);
            }
        });

        mt3.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("main" + ":" + i);
        }
    }
}

class MyThread01 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("m1" + ":" + i);
        }
    }
}

class MyThread02 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("m2" + ":" + i);
        }
    }
}

线程的生命周期

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

在这里插入图片描述

在主函数中用new出来的线程调用sleep() 方法,会让该线程休眠吗?

不会,仍然会让主函数所在线程休眠。

合理地终止线程

 public class StopThreadTest {
    public static void main(String[] args) {
        Stop target = new Stop();
        Thread t1 = new Thread(target);
        t1.start();
        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        target.flag = false;
    }

}

class Stop implements Runnable {
    boolean flag = true;

    @Override
    public void run() {
        for (int i = 1; i < 100; i++) {
            if (flag) {
                System.out.println("开始输出:" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("中止" + Thread.currentThread().getName() + "线程啦!");
                return;
            }
        }
    }
}

线程安全问题的条件

  1. 多线程并发
  2. 有共享数据
  3. 共享数据有修改的行为

怎么解决线程安全问题?

排队执行线程,称为线程同步机制,实际上就是不让线程并发。

对于Java中的三种变量来说

局部变量:永远不会存在线程安全问题,疑问局部变量不共享,一个线程一个栈,可以有多个。

实例变量:在堆中,堆只1个。

静态变量:在方法区,方法区只有一个。

堆和方法区都是线程共享的,所以可能存在线程安全问题。

synchronized的三种写法:

  1. 同步代码块,灵活

    synchronized(线程共享对象:实例变量或者静态变量){
    }
    
  2. 在实例方法上使用synchronized,表示共享对象是this,同步代码块是整个方法体。

  3. 在静态方法上使用,表示共享类锁,类锁只有一把,就算创建100个对象,1000个线程分别访问不同的对象的这个静态方法,实际上相当于访问同一个对象。

避免线程安全问题的写法

尽量用局部变量代替“实例变量和静态变量”

在使用实例变量时,让不同的线程访问不同的对象。

实在不能避免的,只能使用synchronized关键字修饰了。

Object类中的wait()和notify() 方法

wait方法的作用:

Object o = new Object();
o.wait();

表示让正在o对象上活动的线程进入等待状态,无限期等待,直到被唤醒为止。也可以说,o.wait(); 的调用,会让 当前执行这行代码的线程 (也就是正在o上活动的线程)进入等待状态,并且释放之前被占有的o对象的锁。

而o.notify(); 的调用,会唤醒在o对象上等待的线程,让其继续执行。

  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2022-04-15 00:19:38  更:2022-04-15 00:23:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年6日历 -2024/6/2 8:09:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码