JavaSE进阶

IO

FileInputStream / FileReader (字节/字符)输入流抽象类

FileOutputStream / FileWriter (字节/字符)输出流抽象类

常用API
  • 将字节型数组转换字符串 : new String (b, 0 , hasRead )
  •  将字符串转化为字节型数组 : "string".getBytes(); 
    
package wangdezhao.day1.io1;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;


/**
 * @author wangdezhao
 * @date 2021/3/17
 * @description 
 * 		将字节型数组转换字符串 : new String (b, 0 , hasRead )
 *		将字符串转化为字节型数组 : "string".getBytes(); 
 */
public class Test1 {
    public static void main(String[] args) throws IOException {
        // output,input 是字节流, reader,writer是字符流
        // 写入
        FileOutputStream fos = new FileOutputStream("a.txt",true);
        // reader读取
        // FileReader reader = getReader1();
        // input读取
        FileInputStream fis = getFileInputStream(fos);
        System.out.println("文件读取完毕");
        fos.close();
        fis.close();
    }

    private static FileInputStream getFileInputStream(FileOutputStream fos) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        byte[] b = new byte[32];
        int hasRead = 0;
        fos.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8));
        fos.write("1234567890".getBytes(StandardCharsets.UTF_8));
        while((hasRead = fis.read(b))>0){
            // 将字节数组转换成字符串
            System .out.println(new String (b, 0 , hasRead ));
        }
        return fis;
    }

    private static FileReader getReader1() throws IOException {
        FileReader reader = new FileReader("a.txt");
        char[] ch = new char[11];

        while(reader.read(ch) != -1){
            System.out.println(ch);
        }
        ;
        return reader;
    }
}

  • 操作视频,图片,音频等非文本型文件一律使用字符流,和python爬虫类似
package wangdezhao.day1.myfilecopy1;

import java.io.*;
import java.util.Scanner;

/**
 * @author wangdezhao
 * @date 2021/3/17
 * @description 
 * 	1.把a.txt内容复制到b.txt中
 * 	2.把**.jpg内容复制到当前项目目录下的mn.jpg
 */
public class myFileCopy {
    public static void main(String[] args) throws IOException {
        myCopyJpg();
    }

    private static void myCopyJpg() throws IOException {
        System.out.println("请输入要复制的文件名");
        String s1 = new Scanner(System.in).nextLine();
        File f1 = new File(s1);
        System.out.println("请输入要复制到的文件名");
        String s2 = new Scanner(System.in).nextLine();
        File f2 = new File(s2);

        // 新建 f2 输出流
        FileOutputStream fos2 = new FileOutputStream(f2);
        // 读取 f1 输入流
        FileInputStream fis1 = new FileInputStream(s1);
        byte[] bytes = new byte[16];
        int hasRead = 0;
        while ((hasRead = fis1.read(bytes)) > 0) {
            fos2.write(bytes, 0, hasRead);
        }
    }
}

  • RandomAccessFile 任意文件访问( 不建议翻译成随机 )

    RandomAccessFile Java 输入 输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。与普通的输入/输出流不同的是,RandomAccessFile 支持"随机访问"的方式,程序可以直接跳转到文件的任意地方来读写数据。

    OutputStream Writer 等输出流不同的是, RandomAccessFile 允许自由定位文件记录指针,RandomAccessFile 可以不从开始的地方开始输出,因此 RandomAccessFile 可以向己存在的文件后追加内容 如果程序需要向己存在的文件后追加内容,则应该使用RandomAccessFile

    RandomAccessFile 的方法虽然多,但它有 个最大的局限,就是只能读写文件,不能读写其他 IO

    节点

    • long getFilePointer(): 返回文件记录指针的当前位置。

    • void seek(long pos): 将文件记录指针定位到 pos 位置

    • public RandomAccessFile(File file,String mode) throws FileNotFoundException

      “r” 只供阅读。调用结果对象的任何写方法都会导致抛出IOException。

      “rw” 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

      “rws” 可用于读写,并要求对f ile的内容或元数据的每次更新都要同步写入底层存储设备。

      “rwd” 可用于读写,并要求对文件内容的每次更新都要同步写入底层存储设备。

package wangdezhao.day1.randomaccessfile;

import java.io.*;
import java.nio.charset.StandardCharsets;


/**
 * @author wangdezhao
 * @date 2021/3/17
 * @description: 随机访问文件中的指定位置
 */
public class RandomAccessFileTest {
    public static void main(String[] args) {
        try (RandomAccessFile raf = new RandomAccessFile("a.txt", "rw")) {
            int hasRead = 0;
            byte[] data = new byte[64];
            FileOutputStream fos = new FileOutputStream("b.txt");
            System.out.println("pointer初始位置为"+raf.getFilePointer());
            System.out.println("文件长度为"+raf.length());
            // 找到文件末尾,添加第一句话
            raf.seek(raf.length());
            raf.write("111".getBytes(StandardCharsets.UTF_8));

            // 找到文件中间,输出第二句话
            raf.seek(raf.length()/2);
            raf.write("222".getBytes(StandardCharsets.UTF_8));

            // 找到文件的中间,中文文件会直接乱码,说明seek中的pos参数代表的是字节的位置
//            raf.seek(10); // 文件乱码
//            raf.write("插入中文".getBytes(StandardCharsets.UTF_8));

            // 从a.txt读取输出到b.txt
            while((hasRead = raf.read(data))>0){
                fos.write(data,0,hasRead);
            }

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

    }
}

  • BufferedInputStream/BufferedOutputStream

    可以提升效率


	public static void main(String[] args) throws IOException {
		//bufferedOutput();
		//bufferedInput();
		//copyFile();
	}
	/**
	 * 文件复制操作(操作文件最快)
	 * @throws IOException
	 */
	private static void copyFile() throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\test.avi"));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\test.avi"));
		byte[] bytes = new byte[1024];
		int len = 0;
		while((len = bis.read(bytes)) != -1){
			bos.write(bytes, 0, len);
		}
		bos.close();
		bis.close();
		System.out.println("复制完成");
		
	}
	/**
	 * BufferedInputStream:提升读取效率,执行顺序和BufferedOutputStream一样
	 */
	private static void bufferedInput() throws IOException{
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\a.txt"));
		byte[] b = new byte[1024];
		int len = 0;
		while((len = bis.read(b)) != -1){
			System.out.println(new String(b, 0, len));
		}
		bis.close();//关闭缓冲流也会吧字节流关闭
	}
	/**
	 * BufferedOutputStream:提升写入效率
	 * 	没提升效率前顺序:程序->JVM->计算机底层写入(每次读取*字节,每次都调用一次计算机底层)
	 * 提升效率后:程序->JVM(先不调用计算机底层,先将数据缓冲,数据读取完毕后->计算机底层)
	 * OutputStream的子类,使用方法都一样
	 */
	private static void bufferedOutput() throws IOException{
		FileOutputStream fos = new FileOutputStream("D:\\a.txt");
		BufferedOutputStream bos = new BufferedOutputStream(fos);//传入要提升效率的字节输出流
		bos.write("HelloWorld".getBytes());
		bos.close();
		System.out.println("写入完成");
	}


复制文件夹

最核心的就是获取文件目录: File[] results = src.listFiles();

然后使用File的构造方法拼接出子目录/子文件: File des1 = new File(des, src1.getName());

package wangdezhao.day5.copyfile2;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

/**
 * @author wangdezhao
 * @date 2021/3/21
 * @description
 */
public class Demo2 {
    public static void main(String[] args) {
        // 输入拷贝路径和粘贴路径,新建字节输入输出流对象
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入要复制的文件夹路径");
        File src = new File(sc.nextLine());
        System.out.println("请输入要粘贴的文件夹路径");
        File des = new File(sc.nextLine());

        if (src != null) {
            System.out.println(copyDir(src, des) ? "复制成功" : "复制失败");
        } else {
            System.out.println("文件路径不存在");
        }
    }
    // 复制文件夹,递归
    private static boolean copyDir(File src, File des) {
        // 获取文件夹下的目录
        File[] results = src.listFiles();
        if (results != null) { // 如果目录不为空
            for (File src1 : results) {  // 遍历目录
                // 拼接出子文件的文件对象
                File des1 = new File(des, src1.getName());
                // 递归
                if (src1.isDirectory()) { // 如果子文件是目录
                    des1.mkdir();
                    copyDir(src1, des1);
                } else {   //是文件,直接复制
                    copyFile(src1, des1);
                }
            }
            return true;
        } else {
            return false;
        }
    }
    // 复制文件
    private static void copyFile(File src, File des) {
        FileInputStream input = null;
        FileOutputStream output = null;

        try {
            byte[] bytes = new byte[1024];
            int readCount;
            input = new FileInputStream(src);
            output = new FileOutputStream(des);
            while ((readCount = input.read(bytes)) != -1) {
                output.write(bytes, 0, readCount);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                input.close();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WHrRplBI-1617179016612)(/Users/wangdezhao/Pictures/iostream.png)]

多线程编程

进程和线程
  • 进程: 一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
  • 线程: 是在进程的基础上划分的更小的程序单元,线程是在进程上创建并且使用的,所以线程依赖于进程的支持,但是线程的启动速度要比进程的速度快许多,所以当使用多线程进行并发处理的时候,其执行的性能要高于进程。
  • Java是多线程的编程语言,所以在高并发访问处理的时候能实现更好的性能

创建一个线程

Java 提供了三种创建线程的方法:

  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。
1. Thraed 类实现多线程
继承Thread类实现多线程:

Java里面提供了一个java.lang.Thread的程序类,那么一个类只要继承了此类就表示这个类为线程的主体类,但是并不是说这个类就可以直接实现多线程处理了,因为还需要覆写Thread类中提供的一个run()的方法,而这个方法就是线程的主方法。多线程中要执行的功能都必须在run方法中进行定义,正常情况下,如果想要使用一个类中的方法,一定要产生实例化对象然后去调用方法,但是run方法是不能被直接调用的,因为涉及到操作系统资源调度问题,所以想要启动多线程必须使用start()方法完成

public class Test{
    
	public static void main(String[] args){
		//new MyThread("线程A").run();
        //new MyThread("线程A").run();
        //new MyThread("线程A").run();
        
        // 需要使用start()方法开始多线程,用run()还是单线程
        new MyThread("线程A").start();
        new MyThread("线程B").start();
        new MyThread("线程C").start();
	}
    
}

class MyThread extends Threads{
    
	private String title;
	public MyThread(String title){
		this.title = title;
	}
    
	@Override
	public void run(){
		for(int x =0;x<10;x++){
			System.out.println(this.title +"运行,x = "+x);
		}
	}
    
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JnsuDbhn-1617179016613)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210309092402976.png)]

2. 基于Runnable接口实现的多线程

Thread(Runnable threadOb,String threadName);

threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。

public class Test1 {
    public static void main(String[] args) {
        //1.runnable接口 Thread(Runnable threadOb,String threadName);
        new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    System.out.println("线程a = "+i);
                }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("线程b = "+i);
            }
        }).start();
    }
}


// Runnable接口与Thread类继承实现多线程的区别?
// 1. 解决了Thread类继承的单继承的缺陷
// 2. Runnable多个线程可以共享同一个target对象,所以很适合多个相同线程来处理同一份资源的情况

3.Callable和FutureTask实现多线程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQtJH1wI-1617179016614)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210323144402243.png)]

// Thread 调用Start()来开始多线程
public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

// RunnableFuture接口继承 Runnable,Future接口
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}


// FutureTask 实现 Runnable接口
public class FutureTask<V> implements RunnableFuture<V> 

// FutureTask 作为实现类传入 Thread
public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}
    
package com.wangdezhao.day15;

import com.sun.xml.internal.xsom.impl.ForeignAttributesImpl;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author wangdezhao
 * @date 2021/3/15
 * @description
 */
public class Test implements Callable<Integer> {
    public static void main(String[] args) {
        FutureTask<Integer> ft = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int i = 0;
                for (; i < 100; i++) {
                    System.out.println("线程" + Thread.currentThread().getName() + " " + i);
                }
                return i;
            }
        });
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"的循环变量i的值= "+i);
            if (i == 20){
                new Thread(ft,"有返回值的线程").start();
            }
        }
        try{
            System.out.println("子线程的返回值: "+ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 100; i++) {
            System.out.println("线程" + Thread.currentThread().getName() + " " + i);
        }
        return i;
    }
}

Thread 和 Runnable 的关系?

Thread类实现了Runnable接口,Runnable接口里只有一个抽象的run()方法。说明Runnable不具备多线程的特性。Runnable依赖Thread类的start方法创建一个子线程,再在这个子线程里调用run()方法,才能让Runnable接口具备多线程的特性

Thread主类中的run()实现自Runnable接口,其实例化对象只负责描述线程对象,即访问资源的对象

线程实现子类的run()也实现自Runnable接口,其实例化对象则负责描述资源,重写Runnable接口中的run()方法(写完后会覆盖父类的run())

Runnable可以更好地描述出多个线程访问同一资源的结构,虽然也可以通过继承Thread类来实现,但是此时,子类就继承了start()方法,再去用Thread方法中的start去启动就不合适了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Viw5hQgu-1617179016614)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210319083258873.png)]

MyThread mt = new MyThread();
new Thread(mt).start;
new Thread(mt).start;
new Thread(mt).start;
//三个mt 指向同一个Thread类   
class MyThread implements Runnable{
    @Override
    run(){
        // 重写run(),描述资源
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xpvjwQto-1617179016615)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210319090219823.png)]

控制线程

1. join线程

join作用是让其他线程变为等待: t1.join();// 让其他线程变为等待,直到当前t1线程执行完毕,才释放。


生产者-消费者模型

package wangdezhao.day4.multithread;

/**
 * @author wangdezhao
 * @date 2021/3/20
 * @description 1. 生产者负责信息内容的生产
 * 2. 每当生产者完成一项信息之后消费者要从这里面取走信息
 * 3. 如果没生产完,消费者就要等待,反之如果消费者没取走信息,生产者也要等待
 */

/**
 * 2个问题
 * 1.重复输出
 * 2.信息错误了
 */
public class Demo_01 {
    public static void main(String[] args) {
        Message msg = new Message();
        new Thread(new Producer(msg)).start();
        new Thread(new Consumer(msg)).start();
    }
}

class Message {
    /**
     * 如果flag == true 允许生产不允许消费
     * 如果flag == false 允许消费不允许生产
     */
    private boolean flag = true;
    private String title;
    private String content;

    /**
     * @param title
     * @param content set就是生产
     */
    public synchronized void setInfo(String title, String content) {
        if (this.flag == false) {  //如果此时不允许生产,如果允许生产就跳过执行此代码块
            try {
                // 生产就等待
                this.wait();
                //super.wait(); // 继承Object类中的wait()方法
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // flag == true,允许生产
        // 生产过程: 变量赋值
        this.title = title;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.content = content;

        this.flag = false; // 标记此时已经生产结束了,不能再生产应该等待被消费了
        this.notify(); // 此时如果有等待的消费者进程,那么就进行唤醒第一个
//        super.notify();
    }

    /**
     * get就是消费
     *
     * @return String
     */
    public synchronized String getInfo() {
        if (flag == true) {   // 如果此时不允许消费,消费就进入等待,否则跳过此代码块
            try {
                this.wait();
//                super.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

//        // flag ==false 等待被消费
//        try {
//            return this.title + ": " + this.content;
//        } finally {
//            flag = true; //之后不允许在消费了,要准备生产
//           // this.notify();
//            super.notify(); //消费之后要唤醒下一个等待的进程
//        }
        flag = true; //之后不允许在消费了,要准备生产
//        super.notify(); //消费之后要唤醒下一个等待的进程
        this.notify();
        return this.title + ": " + this.content;

    }
}

class Producer implements Runnable {
    private Message msg;

    public Producer(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.msg.setInfo("普通记者", "小道消息!");
            } else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.msg.setInfo("香港记者", "搞了个大新闻!");
            }
        }
    }
}

class Consumer implements Runnable {
    private Message msg;

    public Consumer(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(msg.getInfo());
        }
    }
}
生产者-消费者模型 案例1
package wangdezhao.day5.test5;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author wangdezhao
 * @date 2021/3/21
 * @description 抢答问题 : 设置三个参赛者参加抢答比赛,,同时发出答题指令,抢答成功给出成功提示,失败给出失败提示
 */
public class Test5 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread mt = new MyThread();
        FutureTask<String> futureTaskA = new FutureTask<>(mt);
        FutureTask<String> futureTaskB = new FutureTask<>(mt);
        FutureTask<String> futureTaskC = new FutureTask<>(mt);

        new Thread(futureTaskA, "选手A").start();
        new Thread(futureTaskB, "选手B").start();
        new Thread(futureTaskC, "选手C").start();
        
        // get() 方法用来获得 call()方法的返回值
        System.out.println(futureTaskA.get()); 
        System.out.println(futureTaskB.get());
        System.out.println(futureTaskC.get());

    }
}


class MyThread implements Callable<String> {
    /**
     * flag == true  抢答成功
     * flag == false 抢答失败
     */
    private boolean flag = false;

    @Override
    public String call() throws Exception {
        synchronized (this) {
            if (this.flag == true) {
                this.flag = false;
                return Thread.currentThread().getName() + "抢答成功";
            } else {
                this.flag = true;
                return Thread.currentThread().getName() + "抢答失败";
            }
        }
    }
}

生产者-消费者模型 案例2
package wangdezhao.day5.test4;

import java.util.concurrent.TimeUnit;

/**
 * @author wangdezhao
 * @date 2021/3/21
 * @description 生产电脑和搬运电脑, 每生产一台就搬走一台, 统计生产的台数
 */
public class Test4 {
    public static void main(String[] args) {
        Message msg = new Message();
        Producer producer = new Producer(msg);
        Transporter transporter = new Transporter(msg);
        new Thread(producer,"生产者").start();
        new Thread(transporter,"搬运者").start();

    }
}

class Message {
    /**
     * flag == true 只能生产,不能搬走
     * flag == false 只能搬运,不能生产
     */
    private boolean flag = true;
    private int num = 0;

    public synchronized void producePC() throws InterruptedException {
        if (flag == false) {
            try {
                super.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        TimeUnit.SECONDS.sleep(1);
        // flag == true ,可以生产
        System.out.println(Thread.currentThread().getName()+"生产了一台电脑,总共生产了" + (++num) + "台");
        flag = false;
        super.notify();
    }

    public synchronized void TransportPC() throws InterruptedException {
        if (flag == true) {
            try {
                super.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        TimeUnit.SECONDS.sleep(1);
        // flag == false ,可以搬运
        System.out.println(Thread.currentThread().getName()+"搬走了一台电脑");
        flag = true;
        super.notify();
    }
}

class Producer implements Runnable {
    private Message msg;

    public Producer(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                this.msg.producePC();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Transporter implements Runnable {
    private Message msg;

    public Transporter(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                this.msg.TransportPC();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


生产者-消费者模型 案例3
package wangdezhao.day5.test3;

import com.sun.org.apache.xalan.internal.xsltc.compiler.util.CompareGenerator;

/**
 * @author wangdezhao
 * @date 2021/3/21
 * @description 设计四个线程对象, 两个执行减, 两个执行加
 */
public class Demo {
    public static void main(String[] args) {
        Message msg = new Message();
        ThreadPlus tp = new ThreadPlus(msg);
        ThreadMinus tm = new ThreadMinus(msg);
        new Thread(tm, "减法线程 - A1").start();
        new Thread(tp, "加法线程 - B1").start();
        new Thread(tm, "减法线程 - A2").start();
        new Thread(tp, "加法线程 - B2").start();
    }
}

class Message {
    private int num = 0;
    private boolean flag = true;  // true 只能加不能减, false 只能减不能加

    public synchronized void getMiuns() {
        if (this.flag == true) {   // flag == true  只能加不能减
            try {   // 线程阻塞,等待加法做完
                super.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // flag == false 可以做减法
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("num = " + num--);
        System.out.println(Thread.currentThread().getName() + "进行了减法操作");
        this.flag = true; // 减法做完不能继续做减法了,flag == true
        super.notifyAll(); // 唤醒其他正在等待的线程

    }

    public synchronized void getPlus() {
        if (this.flag == false) {  // flag == false 不能加,要等待减法
            try {
                super.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // flag == true 可以加法
        System.out.println("num = " + num++);
        System.out.println(Thread.currentThread().getName() + "进行了加法操作");
        this.flag = false;//加法做完,等待做减法
        super.notifyAll();
    }
}

class ThreadMinus implements Runnable {
    private Message msg;

    public ThreadMinus(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.msg.getMiuns();
        }
    }
}


class ThreadPlus implements Runnable {
    private Message msg;

    public ThreadPlus(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.msg.getPlus();
        }
    }
}
valotile

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KlmVak8Z-1617179016616)(/Users/wangdezhao/Library/Application Support/typora-user-images/image-20210321171154191.png)]

反射

Class类对象的三种实例化模式
反射获取类结构信息

​ 如果此时想获得该类的一些基础信息则可以通过Class类中的如下方法:
​ 1、获取包名称:public Package getPackage()
​ 2、获取继承父类:public Class<? super T> getSuperclass()
​ 3、获取实现父接口:public Class<?>[] getInterfaces()

反射调用方法

在一个类中除了有继承的关系外,最为重要的操作就是类中的结构处理了,而类中的结构首先需要观察的就是构造方法的使用问题,实际上在之前通过反射实例化对象的时候就已经接触到构造方法的问题了:
实例化方法替代:clazz.getDeclaredConstructor().newInstance()
所有类的构造方法的获取都可以直接通过Class类来完成,该类中定义有如下的几个方法:
获取所有构造方法:
public Constructor<?>[ ] getDeclaredConstructors() throws SecurityException
获取指定构造方法:
public Constructor getConstructor() throws SecurityException
获取所有构造方法:
public Constructor<?>[ ] getConstructors()throws SecurityException
获取指定构造方法:
public Constructor getConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值