序列化和反序列化 
序列化 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;
            }
        }
    }
}
  
线程安全问题的条件 
- 多线程并发
 - 有共享数据
 - 共享数据有修改的行为
   
怎么解决线程安全问题? 
排队执行线程,称为线程同步机制,实际上就是不让线程并发。  
对于Java中的三种变量来说 
局部变量:永远不会存在线程安全问题,疑问局部变量不共享,一个线程一个栈,可以有多个。  
实例变量:在堆中,堆只1个。  
静态变量:在方法区,方法区只有一个。  
堆和方法区都是线程共享的,所以可能存在线程安全问题。  
synchronized的三种写法: 
-  
同步代码块,灵活  synchronized(线程共享对象:实例变量或者静态变量){
}
   -  
在实例方法上使用synchronized,表示共享对象是this,同步代码块是整个方法体。   -  
在静态方法上使用,表示共享类锁,类锁只有一把,就算创建100个对象,1000个线程分别访问不同的对象的这个静态方法,实际上相当于访问同一个对象。     
避免线程安全问题的写法 
尽量用局部变量代替“实例变量和静态变量”  
在使用实例变量时,让不同的线程访问不同的对象。  
实在不能避免的,只能使用synchronized关键字修饰了。  
Object类中的wait()和notify() 方法 
wait方法的作用:  
Object o = new Object();
o.wait();
  
表示让正在o对象上活动的线程进入等待状态,无限期等待,直到被唤醒为止。也可以说,o.wait(); 的调用,会让 当前执行这行代码的线程 (也就是正在o上活动的线程)进入等待状态,并且释放之前被占有的o对象的锁。  
而o.notify(); 的调用,会唤醒在o对象上等待的线程,让其继续执行。 
                
                
                
        
        
    
  
 
 |