• 20145207《Java程序设计》第6周学习总结


    教材学习内容总结

    一.输入/输出

    InputStreamOutputstream

    串流设计的概念

    从应用程序角度看,将数据从来源取出,可以使用输入串流,将数据写入目的地,可以使用输出串流;在Java中,输入串流代表对象为java.io.InputStream实例,输出串流代表对象为java.io.OutputStream实例;

    串流继承框架

    System.in与System.out分别代表标准输入和标准输出;

    可以使用System的setIn()方法指定InputStream实例,用setOut()方法指定printStream;代码如下:

    System.err为printStream实例,称为标准输出串流,用于立即显示错误信息;

    FileInputStream:是InputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可以用来写出数据,主要操作了InputStream的read()抽象方法,从而读取文档中的数据;

    FileOutputStream:是OutputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可以用来写出数据,主要操作了OutputStream中的write抽象方法,使之可写出数据到文档;

    不使用,时都要用close()关闭文档;

    ByteStream是InputStream的子类,可以指定byte数组创建实例,一旦创建就可以将byte数组当做数据源进行读取。ByteArrayOutputStream是OutputStream的子类,可以指定byte数组创建实例,一旦创建就可以将byte数组当做目的地写出数据;

    串流装饰处理器

    若想要为输入输出的数据作加工处理,可以使用打包器类(如:scanner);

    InputStream和OutputStream的一些子类也具有打包器的作用,这些子类创建时,可以接受InputStream和OutputStream实例;

    常用打包器:BufferedInputStream、BufferOutputSream(具备缓冲区作用),DataInputStream、DataOutputStream(具备数据转换处理作用),ObjectInputStream、ObjectOutputStream(具备对象串行化能力)等;代码如下:

    package cc.openhome;

     

    import java.io.*;

     

    public class BufferedIO {

        public static void dump(InputStream src, OutputStream dest)

                                  throws IOException {

            try(InputStream input = new BufferedInputStream(src);

                 OutputStream output = new BufferedOutputStream(dest)) {

                byte[] data = new byte[1024];

                int length;

                while ((length = input.read(data)) != -1) {

                    output.write(data, 0, length);

                }

            }

        }

    }

    package cc.openhome;

     

    import java.io.*;

     

    public class Member {

        private String number;

        private String name;

        private int age;

     

        public Member(String number, String name, int age) {

            this.number = number;

            this.name = name;

            this.age = age;

        }

     

        public String getNumber() {

            return number;

        }

     

        public void setNumber(String number) {

            this.number = number;

        }

     

        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 String.format("(%s, %s, %d)", number, name, age);

        }

       

        public void save() throws IOException {

            try(DataOutputStream output =

                    new DataOutputStream(new FileOutputStream(number))) {

                output.writeUTF(number);

                output.writeUTF(name);

                output.writeInt(age);

            }

        }

       

        public static Member load(String number) throws IOException {

            Member member;

            try(DataInputStream input =

                    new DataInputStream(new FileInputStream(number))) {

                member = new Member(

                        input.readUTF(), input.readUTF(), input.readInt());

            }

            return member;

        }

    }

    package cc.openhome;

     

    import java.io.IOException;

    import static java.lang.System.out;

     

    public class MemberDemo {

        public static void main(String[] args) throws IOException {

            Member[] members = {

                        new Member("B1234", "Justin", 90),

                        new Member("B5678", "Monica", 95),

                        new Member("B9876", "Irene", 88)

            };

            for(Member member : members) {

                member.save();

            }

            out.println(Member.load("B1234"));

            out.println(Member.load("B5678"));

            out.println(Member.load("B9876"));

        }

    }

     

    import static java.lang.System.out;

     

    public class Member2Demo {

        public static void main(String[] args) throws Exception {

            Member2[] members = {new Member2("B1234", "Justin", 90),

                                 new Member2("B5678", "Monica", 95),

                                 new Member2("B9876", "Irene", 88)};

            for(Member2 member : members) {

                member.save();

            }

            out.println(Member2.load("B1234"));

            out.println(Member2.load("B5678"));

            out.println(Member2.load("B9876"));

        }

    }

    字符处理类

    • Reader与Writer继承架构

    java.io.Reader类:抽象化了字符数据读入的来源;

    java.io.Writer类:抽象化了数据写出目的地;代码如下:

    FileReader:读取文档并将读到的数据转换成字符;StringWriter:将字符数据写至它最后使用toString()的方法取得字符串;代码如下:

    import java.io.*;

     

    public class CharUtil {

        public static void dump(Reader src, Writer dest) throws IOException {

            try(Reader input = src; Writer output = dest) {

                char[] data = new char[1024];

                int length;

                while((length = input.read(data)) != -1) {

                    output.write(data, 0, length);

                }

            }

        }

    }

    package cc.openhome;

     

    import java.io.*;

     

    public class CharUtilDemo {

        public static void main(String[] args) throws IOException {

            FileReader reader = new FileReader(args[0]);

            StringWriter writer = new StringWriter();

            CharUtil.dump(reader, writer);

            System.out.println(writer.toString());

        }

    }

    字符处理装饰器

    将字节数据转换成对应的编码字符,可以使用InputStreamReader、OutputStreamWriter对串流数据打包;代码如下:

    import java.io.*;

     

    public class CharUtil2 {

        public static void dump(Reader src, Writer dest) throws IOException {

            try(Reader input = src; Writer output = dest) {

                char[] data = new char[1024];

                int length;

                while((length = input.read(data)) != -1) {

                    output.write(data, 0, length);

                }

            }

        }

       

        public static void dump(InputStream src, OutputStream dest,

                                 String charset) throws IOException {

            dump(

                new InputStreamReader(src, charset),

                new OutputStreamWriter(dest, charset)

            );

        }

     

        // 采用预设编码

        public static void dump(InputStream src, OutputStream dest)

                               throws IOException {

            dump(src, dest, System.getProperty("file.encoding"));

        }

    }

    提高字符输入输出效率,提供缓冲区作用:BufferedReader、BufferWriter;

    printWriter:对OutStream打包,对writer打包;

    .线程与并行API

    线程

    线程简介

    单线程程序:启动的程序从main()程序进入点开始至结束只有一个流程;多线程程序:拥有多个流程;

    java中从main()开始的流程会由主线程执行可以创建Thread实例来执行Runable实例定义的run()方法;代码如下:(龟兔赛跑)

    package cc.openhome;

     

    public class Tortoise implements Runnable {

        private int totalStep;

        private int step;

     

        public Tortoise(int totalStep) {

            this.totalStep = totalStep;

        }

     

        @Override

        public void run() {

            while (step < totalStep) {

                step++;

                System.out.printf("乌龟跑了 %d 步...%n", step);

            }

        }

    }

    import static java.lang.System.out;

     

    public class TortoiseHareRace {

        public static void main(String[] args) {

            boolean[] flags = {true, false};

            int totalStep = 10;

            int tortoiseStep = 0;

            int hareStep = 0;

            out.println("龟兔赛跑开始...");

            while(tortoiseStep < totalStep && hareStep < totalStep) {

                tortoiseStep++;      

                out.printf("乌龟跑了 %d 步...%n", tortoiseStep);

                boolean isHareSleep = flags[((int) (Math.random() * 10)) % 2];

                if(isHareSleep) {

                    out.println("兔子睡着了zzzz");

                } else {

                    hareStep += 2;   

                    out.printf("兔子跑了 %d 步...%n", hareStep);

                }

            }

        }

    }

    public class TortoiseHareRace2 {

        public static void main(String[] args) {

            Tortoise tortoise = new Tortoise(10);

            Hare hare = new Hare(10);

            Thread tortoiseThread = new Thread(tortoise);

            Thread hareThread = new Thread(hare);

            tortoiseThread.start();

            hareThread.start();

        }

    }

    • ThreadRunnable

    创建Thread实例就是为JVM加装CPU,启动额外CPU就是调用实例的start()方法,额外CPU的进入点可以定义在Runable接口的run()方法中;

    除了将流程这样定义,另一个撰写多线程程序的方式就就是继承Thread类,重新定义run()方法;

    操作Runnable接口的好处就是较有弹性,你的类还有机会继承其他类;若继承了Thread类,通常是为了直接利用Thread中定义的一些方法;

    线程生命周期

    Daemon线程:如果一个Thread被标示为Deamon线程,在所有的非Deamon线程都结束时,JVM就会自动终止;代码如下:

    public class DaemonDemo {

     

        public static void main(String[] args) {

            Thread thread = new Thread(() -> {

                while (true) {

                    System.out.println("Orz");

                }

            });

            // thread.setDaemon(true);

            thread.start();

        }

    }

    Thread基本状态图:可执行、被阻断、执行中;

    线程看起来但事实是同一个时间点上,一个CPU还是只能执行一个线程,只是因其不断切换且很快,所以看起来像是同时执行;

    线程有其优先权,setPriority()方法设定优先权,利用多线程改进效能;

    当线程使用join()加入另一线程时,另一线程会等待被加入的线程工作完毕再继续它的动作;代码如下:

    线程完成run()方法后,就会进入Dead,此时不可以再调用start()方法否则会抛出IlligleThreadException;

    关于ThreadGroup

    每个线程都属于某个线程群组,线程一旦归入某个群组,就无法再更换;可以使用以下程序片段取得当前线程所属线程群组名:Thread.currentThread().getThreadGroup().getname();

    使用uncoughtException()方法处理群组中某个线程出现异常未被捕捉的情况,可以重新定义此方法;
    代码如下:

    package cc.openhome;

     

    public class ThreadGroupDemo {

     

        public static void main(String[] args) {

            ThreadGroup group = new ThreadGroup("group") {

                @Override

                public void uncaughtException(Thread thread, Throwable throwable) {

                    System.out.printf("%s: %s%n",

                            thread.getName(), throwable.getMessage());

                }

            };

     

            Thread thread = new Thread(group, () -> {

                throw new RuntimeException("测试例外");

            });

     

            thread.start();

        }

    }

    public class ThreadGroupDemo2 {

     

        public static void main(String[] args) {

            ThreadGroup group = new ThreadGroup("group");

           

            Thread thread1 = new Thread(group, () -> {

                throw new RuntimeException("thread1 测试例外");

            });

            thread1.setUncaughtExceptionHandler((thread, throwable) -> {

                System.out.printf("%s: %s%n",

                        thread.getName(), throwable.getMessage());

            });

     

            Thread thread2 = new Thread(group, () -> {

                throw new RuntimeException("thread2 测试例外");

            });

     

            thread1.start();

            thread2.start();

        }

    }

    • synchronizedvolatile

    如果在方法上标示synchronized,则执行方法必须取得该实例的锁定,才能执行该区块内容;

    可重入同步:线程取得某对象锁定后,若执行过程中又要执行synchronized,尝试取得锁定的对象来源又是同一个,则可以直接执行;

    synchronized:互斥性:该区块同时间只能有一个线程,可见性:线程离开该区块后,另一线程接触到的就是上一线程改变后的对象状态;

    在java中对于可见性的要求,可以使用volatile达到变量范围,在变量上声明volatile,表示变量是不稳定、易变的,也就是可能在多线程下存取,其存取一定是在共享内存中进行,代码如下:

    package cc.openhome;

     

    class Variable1 {

        static int i = 0, j = 0;

     

        static void one() {

            i++;

            j++;

        }

     

        static void two() {

            System.out.printf("i = %d, j = %d%n", i, j);

        }

    }

     

    public class Variable1Test {

        public static void main(String[] args) {

            Thread thread1 = new Thread(() -> {

                while (true) {

                    Variable1.one();

                }

            });

            Thread thread2 = new Thread(() -> {

                while (true) {

                    Variable1.two();

                }

            });

           

            thread1.start();

            thread2.start();

        }

    }

    class Variable2 {

        static int i = 0, j = 0;

     

        static synchronized void one() {

            i++;

            j++;

        }

     

        static synchronized void two() {

            System.out.printf("i = %d, j = %d%n", i, j);

        }

    }

     

    public class Variable2Test {

        public static void main(String[] args) {

            Thread thread1 = new Thread(() -> {

                while (true) {

                    Variable2.one();

                }

            });

            Thread thread2 = new Thread(() -> {

                while (true) {

                        Variable2.two();

                    }

            });

           

            thread1.start();

            thread2.start();

        }

    }

    class Variable3 {

        volatile static int i = 0, j = 0;

     

        static void one() {

            i++;

            j++;

        }

     

        static void two() {

            System.out.printf("i = %d, j = %d%n", i, j);

        }

    }

     

    public class Variable3Test {

        public static void main(String[] args) {

            Thread thread1 = new Thread(() -> {

                while (true) {

                    Variable3.one();

                }

            });

            Thread thread2 = new Thread(() -> {

                while (true) {

                    Variable3.two();

                }

            });

            thread1.start();

            thread2.start();

        }

    }

    等待与通知

    调用锁定对象的wait()方法,线程会释放对象锁定,并进入对象等待集合而处于阻断状态,其他线程可以竞争对象锁定,取得锁定的线程可以执行synchronized范围的代码;

    被竞争的对象调用notify()方法时,会从对象等待集合中随机通知一个线程加入排班,再次执行synchronized前,被通知的线程会与其他线程共同竞争对象锁定;代码如下:

    public class Consumer implements Runnable {

        private Clerk clerk;

       

        public Consumer(Clerk clerk) {

            this.clerk = clerk;

        }

       

        public void run() {

            System.out.println("消费者开始消耗整数......");

            for(int i = 1; i <= 10; i++) {

                try {

                    clerk.getProduct();

                } catch (InterruptedException ex) {

                    throw new RuntimeException(ex);

                }

            }

        }

     }

    public class Clerk {

        private int product = -1;

     

        public synchronized void setProduct(int product) throws InterruptedException {

            waitIfFull();

            this.product = product;

            System.out.printf("生产者设定 (%d)%n", this.product);

            notify();

        }

     

        private synchronized void waitIfFull() throws InterruptedException {

            while (this.product != -1) {

                wait();

            }

        }

     

        public synchronized int getProduct() throws InterruptedException {

            waitIfEmpty();

            int p = this.product;

            this.product = -1;

            System.out.printf("消费者取走 (%d)%n", p);

            notify();

            return p;

        }

     

        private synchronized void waitIfEmpty() throws InterruptedException {

            while (this.product == -1) {

                wait();

            }

        }

    }

    public class ProducerConsumerDemo {

        public static void main(String[] args) {

            Clerk clerk = new Clerk();

            new Thread(new Producer(clerk)).start();

            new Thread(new Consumer(clerk)).start();

        }   

    }

    并行API

    • lockReadWriteLockCondition

    java.util.concurrent.locks包中提供Lock、ReadWriteLock、Condition接口以及相关操作类,可以提供类似synchronized、wait()、notify()、notifyall()的作用,以及更多高级功能;

    Lock接口主要操作类之一为ReentrantLook,可以达到synchronized的作用,也提供额外功能;代码如下:

    ReadWriteLock:如果已经有线程取得Lock对象锁定,尝试再次锁定同一Lock对象是可以的。想要锁定Lock对象,可以调用Lock()方法;

    Condition接口用来搭配Lock,一个Condition对象可代表一个等待集合,可以重复调用Lock的newCondition(),取得多个Condition实例,这代表了有多个等待集合;

    使用Executor

    java.util.concurrent.Executor接口,目的是将Runnable的指定与实际如何执行分离,Executor接口只定义了一个execute();

    像线程池这样类服务的行为,实际上是定义在Executor的子接口java.util.concurrent.ExecutorService当中,通用的ExecutorService由抽象类AbstractExecutorService操作,如果需要线程池的功能,则可以使用其子类java.util.concurrent.ThreadPoolExecutor;

    ExecutorService还定义了submit()、invokeAll()、invokeAny()等方法,这些方法中出现了java.util.concurrent.Future、java.util.concurrent.Callable接口;

    ScheduledExecutorService为ExecutorService的子接口,可以进行工作排程,schedule()方法用来排定Runnable或Callable实例延迟多久执行一次,并返回Future子接口ScheduledFuture的实例;

    并行Collection简介

    java.util.concurrent包中,提供一些支持并行操作的Collection子接口与操作类;

    CopyOnWriteArrayList操作了List接口,这个类的实例在写入操作时,内部会建立新数组,并复制原有数组索引的参考,然后在新数组上进行写入操作,写入完成后,再将内部原参考旧数组的变量参考至新数组;

    CopyOnWriteArraySet操作了Set接口,内部使用CopyOnWriteArrayList来完成Set的各种操作,因此一些特性与CopyOnWriteArrayList是相同的;

    BlockingQueue是Queue的子接口,新定义了put()、take()方法;代码如下:

     

    import java.util.concurrent.BlockingQueue;

     

    public class Producer3 implements Runnable {

        private BlockingQueue<Integer> productQueue;

       

        public Producer3(BlockingQueue<Integer> productQueue) {

            this.productQueue = productQueue;

        }

       

        public void run() {

            System.out.println("生产者开始生产整数......");

            for(int product = 1; product <= 10; product++) {

                try {

                    productQueue.put(product);

                    System.out.printf("生产者提供整数 (%d)%n", product);

                } catch (InterruptedException ex) {

                    throw new RuntimeException(ex);

                }

            }      

        }

    }

    import java.util.concurrent.BlockingQueue;

     

    public class Consumer3 implements Runnable {

        private BlockingQueue<Integer> productQueue;

       

        public Consumer3(BlockingQueue<Integer> productQueue) {

            this.productQueue = productQueue;

        }

       

        public void run() {

            System.out.println("消费者开始消耗整数......");

            for(int i = 1; i <= 10; i++) {

                try {

                    int product = productQueue.take();

                    System.out.printf("消费者消耗整数 (%d)%n", product);

                } catch (InterruptedException ex) {

                    throw new RuntimeException(ex);

                }

            }

        }

     }

    import java.util.concurrent.*;

     

    public class ProducerConsumerDemo3 {

        public static void main(String[] args) {

            BlockingQueue queue = new ArrayBlockingQueue(1);

            new Thread(new Producer3(queue)).start();

            new Thread(new Consumer3(queue)).start();

        }   

    }

     

    教材学习中的问题和解决过程

    这两章的内容是最多的,是真的多!但是在理解方面,并没有前面的概念性的知识难理解,在将书上的代码敲过一些之后,对于这些内容就有了很好地理解。但是书上东西实在太多了!!!好了不说了,焊电表去了

    代码调试中的问题和解决过程

    p345,有关volatile部分不太清楚。
    package cc.openhome;

    class Variable1 {
        static int i = 0, j = 0;
     
        static void one() {
            i++;
            j++;
        }
     
        static void two() {
            System.out.printf("i = %d, j = %d%n", i, j);
        }
    }
     
    public class Variable1Test {
        public static void main(String[] args) {
            Thread thread1 = new Thread(() -> {
                while (true) {
                    Variable1.one();
               }
                });
        Thread thread2 = new Thread(() -> {
                while (true) {
                    Variable1.two();
                }
            });
        
            thread1.start();
            thread2.start();
        }
    }

    j远大于i不理解,晚上我问问同学。

  • 相关阅读:
    java调用打印机方式二
    java调用系统打印机
    Centos7开放端口(永久)
    java毫秒级别定时器
    java计算接口调用时间
    java实现当前时间往前推N小时
    java注解日志记录到数据库
    Java后端HttpClient Post提交文件流 及服务端接收文件流
    springboot整合websocket
    注解@Slf4j使用
  • 原文地址:https://www.cnblogs.com/20145207lza/p/5830720.html
Copyright © 2020-2023  润新知