zoukankan      html  css  js  c++  java
  • 20155217 2016-2017-2 《Java程序设计》第6周学习总结

    20155217 2016-2017-2 《Java程序设计》第6周学习总结

    教材学习内容总结

    第十章
    InputStream与OutputStream
    10.1.1串流设计的概念

    Java将输入/输出抽象化为串流,数据有来源及目的地,衔接两者的是串流对象。

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

    dump()方法接受InputStream与OutputStream实例,分别代表读取数据的来源,以及输出数据的目的地。

    FileIntputStream是InputStream的子类,用于衔接文档以读入数据,FileOutStream是OutputStream的子类,用于衔接文档以写出数据。
    在不使用InputStream与OutputStream时,必须使用close()方法关闭串流。由于InputStream与OutputStrem操作了java.io.Closeable接口,其父接口为java.lang.AutoCloseable接口。

    10.1.2串流继承结构

    1.标准输入/输出

    可以使用System的setIn()方法指定InputStream实例,重新指定标准输入来源。

    可以使用System的setOut()方法指定```printStream````实例,将结果输出至指定的目的地。

    2.FileInputStream与FileOutputStream

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

    FileOutputStream是OutputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可以用来写出数据。主要操作了OnputSream的write()抽象方法,可以写出数据至文档。

    无论FileInputStream还是FileOutputStream,在读取、写入文档时是以字节为单位,通常会使用一些高阶类进行打包,不使用时都要使用close()关闭文档。

    3.ByteArryInputStream与ByteArryOutputStream

    ByteArryInputStream是InputStrteam的子类,可以指定byte数组创建实例,一旦创建就可将byte数组当做数据源进行读取。

    ByteArryOutputStream是OutputStream的子类,可以指定byte数组创建实例,一旦创建将byte数组当做目的地写出数据。

    10.1.3串流处理装饰器

    InputStream、OutStream提供串流基本操作,如果想要为输入/输出的数据做加工处理,则可以使用打包器类。

    常用的打包器具备缓冲区作用的BufferedIutputStream、BufferedOnputStream,具备数据转换处理的DataInputStream、DataOutputStream,具备对象串行化能力的ObjectInputStream、ObjectOutputStream等。

    10.2字符处理类
    10.2.1Reader与Writer继承架构

    针对字符数据的读取,Java SE提供了java.io.Reader类,其抽象化了字符数据读入的来源。

    针对字符数据的写入,则提供了java.io.Writer类。其抽象化了数据写出的目的地。

    -StringReader可以将字符串打包,当作读取来源,StringWriter则可以作为写入目的地,最后用toString()取得所有写入的字符组成的字符串。

    FileReader、FileWriter则可以对文档做读取与写入,读取或写入时默认会使用操作系统默认编码来做字符转换。

    在启动JVM时,可以指定-Dfile.encoding来指定FileReader、FileWriter所使用的编码。

    10.2.2字符处理装饰器

    1.InputStreamReader与OutputStreamWriter

    InputStreamReader、OutputStreamWriter对串流数据打包。

    2.BufferedReader与BufferedWriter

    BufferedReader、BufferedWriter可对Reader、Writer提供缓冲区作用,在处理字符输入/输出时,对效率也会有所帮助。

    3.PrintWriter

    PrintWriter、PrintStream使用上极为类似,不过除了可以对OutputStream打包之外,PrintWriter还可以对Writer进行打包,提供print()、println()、format()等方法。

    部分代码:

    • Member类可以调用save()储存Member实例本身的数据,文件名为Member的会员号码,调用Member.load()指定会员号码,则可以读取文档中的会员数据。
    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"));
        }
    }
    

    运行结果:

    第十一章
    11.1线程
    11.1.1线程简介

    在java中,如果想在main()以外独立设计流程,可以撰写类操作java.lang.Runnable接口,流程的进入点是操作在run()方法中。

    在java中,从main()开始的流程会由主线程执行,可以创建Thread实例来执行Runnable实例定义的run()方法。

    10.1.2Thread与Runnable

    JVM是台虚拟计算机,只安装一颗称为主线程的CPU,可执行main()定义的执行流程。如果想要为JVM加装CPU,就是创建Thread实例,要启动额外CPU就是调用Thread实例的start()方法,额外CPU执行流程的进入点,可以定义在Runnale接口的run()方法中。

    撰写多线程程序的方式:
    1.将流程定义在Runnable的run()方法中。
    2.继承Thread类,重新定义run()方法。

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

    11.1.3线程生命周期

    1.Daemon线程

    主线程会从main()方法开始执行,直到main()方法结束后停止JVM。

    如果主线程中启动了额外线程,默认会等待被启动的所有线程都执行完run()方法才中止JVM。

    setDeamon()方法用来设定一个线程是否为Daemon线程。

    如果没有使用setDeamon()设定为true,则程序会不断的输出Orz而不终止。

    isDaemon()方法可以判断线程是否为Daemon线程。

    2.Thread基本状态图

    在调用Thread实例start()方法后,基本状态为可执行(Runnable)、被阻断(Blocked)、执行中(Running)。

    3.安插线程

    当线程使用join()加入至另一个线程时,另一个线程会等待被加入的线程工作完毕,然后在继续它的动作,join()的意思表示将线程加入称为另一个线程的流程中。

    4.停止线程

    线程完成run()方法后,就会进入Dead,进入Dead的线程不可以再次调用start()方法,否则会抛出IllegalThreadStateException。

    11.1.4关于ThreadGroup

    获取目前线程所属线程群组名:Thread.currentThread().getThreadGroup().getName()

    ThreadGroup的某些方法,可以对群组中所有线程产生作用。interrupt()方法可以中断群组中所有线程,setMaxPriority()方法可以设定群组中所有线程最大优先权。activeCount()方法获取群组的线程数量 。

    未捕捉异常会由线程实例setUncaughtExceptionHandler()设定的Thread.UncaughtExceptionHandler实例处理之后是线程ThreadGroup,然后是默认的Thread.UncaughtExceptionHandler。

    11.1.5synchronized与volatile

    1.使用synchronized

    每个对象都会有个内部锁定,或称为监控锁定。被标示为synchronized的区块将会被监控,任何线程要执行synchronize区块都必须先取得指定的对象锁定。

    java的synchronize提供的是可重入同步,也就是线程取得某对象锁定后,若执行过程总又要执行synchronize,尝试取得锁定的对象来源又是同一个,则可以直接执行。

    由于线程无法取得锁定时会造成阻断,不正确地使用synchronize有可能造成效能低下,另一个问题则是死结。

    2.使用volatile

    synchronized要求达到的所标示区域的互斥性和可见性。互斥性是指synchronized区块同时间只能有一个线程;可见性是指线程离开synchronized区块后,另一线程接触到的就是上一线程改变后的对象状态。

    可以在变量上声明volatile,标示变量是不稳定、易变的,也就是可能在多线程下存取,这保证变量的可见性,也就是若有线程变动了变量值,另一线程一定可看到变更。被标示为volatile的变量,不允许线程快取,变量值的存取一定是在共享内存中进行。

    volatile保证的是单一变数的可见性,线程对变量的存取一定是在共享内存中,不会在自己的内存空间中快取变量,线程对共享内存中变量的存取,另一线程一定看得到。

    11.1.6等待与通知

    wait()、notify()、notifyAll()是Object定义的方法,可以通过这3个方法控制线程释放对象的锁定,或者通知线程参与锁定竞争。

    线程要进入synchronized范围前,要先取得指定对象的锁定。执行synchronized范围的程序代码期间,若调用锁定对象的wait()方法,线程会释放对象锁定,并进入对象等待集合而处于阻断状态。

    放在等待集合的线程不会参与CPU排班,wait()可以指定等待时间,时间到之后线程会再次加入排班,如果指定时间0或不指定,则线程会持续等待,知道被中断或是告知可以参与排班。

    wait()一定要在条件式成立的循环中执行。

    11.2并行API使用Lock使用ReadWriteLock
    11.2.1Lock、ReadWriteLock与Condition

    1.使用Lock

    lock接口主要操作类之一为ReentrantLock,可以达到synchronized的作用。

    想要锁定Lock对象,可以调用其lock()方法,只有取得Lock对象锁定的线程才可以继续往后执行程序代码,要解除锁定可以调用Lock对象的unclock()。

    Lock接口还定义了tryLock()方法,如果线程调用tryLock()可以取得锁定会返回true,若无法取得锁定并不会发生阻断,而是返回false。

    2.使用ReadWriteLock

    ReadWriteLock接口定义了读取锁定与写入锁定行为,可以使用readLock()、`writeLock()方法返回Lock```操作对象。

    ReentrantReadWriteLock是ReadWriteLock接口的主要操作类,readLock()方法会返回ReentrantReadWriteLock.ReadLock实例,writeLock()犯法会返回ReentrantReadWriteLock.WriteLock实例。

    3.使用StampedLock

    StampedLock类可支持了乐观读取操作。也就是若读取线程很多,写入线程很少的情况下,你可以乐观地认为,写入与读取同时发生的机会很少,因此不悲观的使用哇暖的读取锁定,程序可以查看数据读取之后,是否遭到写入线程的变更,再采取后续的措施。
    4.使用Condition

    Condition接口用来搭配Lock,最基本用法就是达到Object的wait()、notify()、notifyAll()方法的作用。

    Condition的await()、signal()、signalAll()方法,可视为Object的wait()、notify()、notifyAll()方法的对应。

    11.2.2使用Executor

    Runnable用来定义可执行流程与可使用数据,Thread用来执行Runnable。
    将Runnable指定给Thread创建之用,并调用start()开始执行。
    定义了java.util.concurrent.Executor接口,目的是将Runnable指定与实际执行分离。

    1.使用ThreadPoolExecutor

    线程池这类服务的行为实际上是定义在Executor的子接口java.util.concurrent.ExecutorService中。
    通用的java.util.concurrent.Executor的newCacheThreadPool()、newFixedThreadPool()静态方法来创建ThreadPoolExecutor实例,程序看起来较为清楚且方便。

    2.使用ScheduledThreadPoolExecutor

    ScheduledExecutorService为ExecutorService的子接口,顾名思义,可以让你进行工作排程。
    schedule()方法用来排定Runnable或Callable实例延迟多久后执行一次,并返回Future子接口ScheduledFuture的实例。
    对于重复性的执行,可使用scheduleWithFixedDelay()和scheduleAtFixedRate()方法。

    3.使用ForkJoinPool

    ForkJoinPool与其他的ExecutorService操作不同的地方在于,它是闲聊了工作窃取演算,其建立的线程如果完成手边任务,会尝试寻找并执行其他任务建立的资额任务,让线程保持忙碌状态,有效利用处理器的能力。
    ForkJoin框架适用于计算密集式的任务,较不适合用于容易造成线程阻断的场合。

    11.2.3并行Collection简介

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

    CopyOnWriteArraySet操作了Set接口,与CopyOnWriteArrayList相似。

    BlockedQueue是Queue的子接口,新定义了put()、take()方法。

    ConcurrentMap是Map的子接口,其定义了putIfAbsent()、remove()、replace()等方法。这些方法都是原子操作。

    ConcurrentHashMap是ConcurrentMap的操作类,ConcurrentNavigableMap是ConcurrentMap的子接口,其操作类为ConcurrentSkipListMap,可视为支持并行操作的TreeMap版本。

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

    • 问题1:

      运行教材314面MemberDemo.java程序时产生了如果将out.println(Member.load("B1234"));中的B1234改为名字能不能正确输出的想法,于是进行尝试。

    out.println(Member.load("Justin"));
    out.println(Member.load("Monica"));
    out.println(Member.load("Irene"));
    

    结果出现了错误。

    • 问题1解决方案:

      通过检索错误并且重新看书上的内容,发现有可能是因为Memeber类中文件名为Member的会员号码,而不是会员姓名,导致调用Member.laod()类时只能指定号码而出现错误。于是对Member.java中的程序进行如下修改。

    public void save() throws IOException {
    try(DataOutputStream output =
                new DataOutputStream(new FileOutputStream(name))) {
        output.writeUTF(number);
        output.writeUTF(name);
        output.writeInt(age);
    }
    }
    
    public static Member load(String number) throws IOException {
        Member name;
        try(DataInputStream input =
                    new DataInputStream(new FileInputStream(number))) {
            name = new Member(
                    input.readUTF(), input.readUTF(), input.readInt());
        }
        return name;
    }
    

    再次尝试,成功运行。

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

    • 问题1:

      ThreadGroupDemo2.java运行结果与教材中给出的结果不相同。

    • 问题1解决方案:

      首先我检查了自己的代码,发现并没有敲错或者和原代码不相同的地方。
      其次深入理解代码,t1设定了Thread.UncaughtException实例,未捕捉的异常在运行时以

    形式呈现是正确的,因此我认为是教材给的结果出错了。

    代码托管

    上周考试错题总结

    这周的试题不会的很多都属于代码方面的,现在对我来说这些问题是比较难的,以后还是需要多练多总结。

    结对及互评

    评分标准

    1. 正确使用Markdown语法(加1分):

      • 不使用Markdown不加分
      • 有语法错误的不加分(链接打不开,表格不对,列表不正确...)
      • 排版混乱的不加分
    2. 模板中的要素齐全(加1分)

      • 缺少“教材学习中的问题和解决过程”的不加分
      • 缺少“代码调试中的问题和解决过程”的不加分
      • 代码托管不能打开的不加分
      • 缺少“结对及互评”的不能打开的不加分
      • 缺少“上周考试错题总结”的不能加分
      • 缺少“进度条”的不能加分
      • 缺少“参考资料”的不能加分
    3. 教材学习中的问题和解决过程, 一个问题加1分

    4. 代码调试中的问题和解决过程, 一个问题加1分

    5. 本周有效代码超过300分行的(加2分)

      • 一周提交次数少于20次的不加分
    6. 其他加分:

      • 周五前发博客的加1分
      • 感想,体会不假大空的加1分
      • 排版精美的加一分
      • 进度条中记录学习时间与改进情况的加1分
      • 有动手写新代码的加1分
      • 课后选择题有验证的加1分
      • 代码Commit Message规范的加1分
      • 错题学习深入的加1分
      • 点评认真,能指出博客和代码中的问题的加1分
      • 结对学习情况真实可信的加1分
    7. 扣分:

      • 有抄袭的扣至0分
      • 代码作弊的扣至0分
      • 迟交作业的扣至0分

    点评模板:

    • 博客中值得学习的或问题:

      • xxx
      • xxx
      • ...
    • 代码中值得学习的或问题:

      • xxx
      • xxx
      • ...
    • 基于评分标准,我给本博客打分:XX分。得分情况如下:xxx

    • 参考示例

    点评过的同学博客和代码

    • 本周结对学习情况

      • 20155236
      • 结对照片
      • 结对学习内容
        • 字符处理类中Reader与Writer继承架构
        • 线程中Thread与Runnable;
        • 并行API中使用Lock和ReadWriteLock
    • 上周博客互评情况

    其他(感悟、思考等,可选)

    每周的学习都是新的挑战,随着学习的深入,出现了越来越多的相似的名次或者类似的定义,从中我都能总结出一些学习Java的经验和道理,尝试做笔记,将重要的、容易混淆的知识点都记下来,在博客中总结本周学习内容时也着重进行这方面的整理,发现这样在回顾知识点时很方便,也很清晰明了,省去了反复翻看前面书的内容的繁琐,节约了大量的时间。这就是磨刀不误砍柴工的道理了吧。

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 5/5 1/4 20/20
    第二周 140/145 1/5 18/38
    第三周 330/451 1/6 16/54
    第四周 578/1038 1/7 18/72
    第五周 774/1472 1/8 18/90
    第六周 1592/3064 1/9 18/108

    尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
    耗时估计的公式
    :Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。

    参考:软件工程软件的估计为什么这么难软件工程 估计方法

    • 计划学习时间:25小时

    • 实际学习时间:18小时

    • 改进情况:学会了对重点内容做笔记,这样以后查找会比较方便。

    (有空多看看现代软件工程 课件
    软件工程师能力自我评价表
    )

    参考资料

  • 相关阅读:
    pat 甲级 1065. A+B and C (64bit) (20)
    pat 甲级 1064. Complete Binary Search Tree (30)
    pat 甲级 1010. Radix (25)
    pat 甲级 1009. Product of Polynomials (25)
    pat 甲级 1056. Mice and Rice (25)
    pat 甲级 1078. Hashing (25)
    pat 甲级 1080. Graduate Admission (30)
    pat 甲级 团体天梯 L3-004. 肿瘤诊断
    pat 甲级 1099. Build A Binary Search Tree (30)
    Codeforce 672B. Different is Good
  • 原文地址:https://www.cnblogs.com/yangdi0420/p/6658701.html
Copyright © 2011-2022 走看看