Think in Java 读书笔记(三)(浓缩出精华_(:з)∠)_)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/breavo_raw/article/details/75043563

7月份简直热的不行…这种天气出门真的好吗?
写道最后的时候发现,这个读书笔记到三就结束了,说实话,整本TIJ我没有全部看完,还是有一部分的省略的,大概是出于时间上的问题?不过以后有机会的话,会补上的。
言归正传,继续记录今天的笔记~同样会在中间放上一些轻松的小话题~

第15章

1、一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。p352

2、多态算是一种泛化机制,例如,你可以将方法的参数类型设为基类,那么该方法就可以接受这个基类导出的任何类作为参数。

3、泛型实现了参数化类型的概念。

4、Java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。

5、元组:将一组对象直接打包存储于其中的一个单一对象。p354

6、使用泛型创建元组。p356

7、泛型也可以应用于接口,例如生成器,这是一种专门负责创建对象的类。

8、Java泛型的一个局限性;基本类型无法作为类型参数(不过有自动打包,拆包功能)

9、又遇适配器模式。p360

10、无论如何,只要你能做到,你就应该使用泛型化方法。p361

11の迷之消失(:з)∠)

12、类型参数判断;p361

13、泛型方法与可变参数能够很好的共存。

14、一个通用的Generator;p364

15、泛型方法的定义,只需将泛型参数置于返回值之前。
——e.g. public <T> void f(T x){ }

16、泛型应用内部类及匿名内部类;p369

17、在泛型代码内部,无法获得任何有关泛型参数类型的信息。(残酷的事实_(:з)∠)_)

18、Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。p373

19、类型擦除的一些坑;p374

20、泛型类型擦除将擦除到它的第一个边界。(可能有多个边界,稍后就会看到)p375

21、当你希望使用的类型参数比某个具体类型更加“泛化”时,使用泛型才会有所帮助。

22、擦除是Java泛型实现的一种折中(一部分原因是因为泛型并不是Java语言时就有的组成部分)p375

23、泛型类型只有在静态类型检查期间才出现。之后,程序中的所有泛型都将被擦除,替换为他们的非泛型上界。p376

24、向后兼容,迁移兼容。p376

25、擦除的代价:泛型不能用于显式的引用运行时类型的操作,例如,转型,instanceof和new,因为所有关于参数的类型信息都丢失了。

26、在泛型中创建数组,使用Array.newInstance( )是推荐的方式。p378

27、泛型中所有的动作都发生在边界外——对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。
“边界就是发生动作的地方”p380

28、引入类型标签对擦除进行补偿。p380

29、不能创建泛型数组,一般的解决方案是在任何想要创建泛型数组的地方,使用ArrayList;
——这样就可以获得数组的行为,以及由泛型提供的编译期的安全。p383

30、ClassCaseException p383

31、成功创建数组的唯一方式是创建一个被擦出类型的新数组。p384

32、一个简单的泛型数组包装器。p384

33、边界使得你可以在用于泛型的参数类型上设置限制条件。java泛型重用了extends来执行这种限制。p387

34、通配符被限制为单一边界。p389

35、ArraystoreException p390

36、运用泛型可以将上述(35)异常检查移至编译期。p390

37、与数组不同,泛型没有内建的协变类型。
——但是,有时你想在两个类型之间建立某种类型的向上转型关系,这正是通配符所允许的。p391

38、逆变,使用超类型通配符。p393

39、无界通配符;p395

40、<?>表达一种声明:“我是想用Java的泛型来编写这段代码,我在这里并不是要用原生类型,但是在当前这种情况下,泛型参数可以持有任何类型”

41、当处理多个泛型参数时,有时允许一个参数可以是任何类型,同时为其他参数确定某种特定类型的这种能力会显得很重要。

42、由于泛型参数将擦除到他得第一个边界,因此List<?>看起来等价于List<-object->
——1)List实际上表示“持有任何object类型得原生list”
——2)而List<?>表示“具有某种特定类型”得非原生类型,只是我们不知道那种类型是什么。p396

43、一个关于通配符得各种用法得小例子。p396

44、一些关于Java泛型时会出现的各类问题。p400

45、泛型的擦除要求指定可能会用到的泛型的具体边界。p417

46、鸭子类型(潜在类型机制)p417

47、python(动态类型语言,所有类型检查都发生在运行时)
——C++是静态类型语言,类型检查发生在编译期。

48、python演示鸭子类型(潜在类型)p417
——perform( )不关心其参数的类型,因此我可以向它传递任何对象,只要该对象支持speak()和sit()方法。p418

49、c++和python都是强类型的。p418

50、java的泛型有时被称为“第二类泛型”

51、Java实现48项的示例。p419

52、使用反射机制实现潜在类型机制。p420

53、泛型是Java语言发布10年后才引入的。以至于泛型迁移的问题特别多。
——(TIJ也是花费了100多页才解释清楚Java的泛型。)

54、第15章总结;p430

看点文章放松一下:
知乎:华为是一家怎样的公司?

第18章

1、对于程序语言设计者来说,创建一个好的I/O系统是一项艰难的任务。p525

2、I/O系统中存在各种I/O源端和想要与之通信的接收端(文件,控制台,网络链接等)。而且还需要以多种不同的方式与他们通信(顺序, 随机存取, 缓冲, 二进制, 按字符, 按行, 按列)

3、学习I/O系统的过程中,需要学习相当数量的类…p525

4、File类;p525 (也许叫FilePath更好)

5、(回顾)匿名内部类中的参数必须是final的,这样它才能够使用来自该类范围之外的对象。p527

6、File类不仅仅只是代表存在的文件或者目录。也可以~
——①用file对象来创建新的目录或尚不存在的整个目录路径。
——②查看文件特性
——③检查File对象的属性
——④删除,还有其他的一些操作;p532

7、输入和输出;p533

8、编程语言的I/O类库中尝试用六这个抽象概念。p533

9、设计者们限定所有与输入有关的所有类都应该从InputStream继承,而所有输出有关的所有类都应该从OutputStream继承。

10、产生输入的数据源:
——1)字节数组
——2)string对象
——3)文件
——4)管道
——5)其他;p534

11、InputStream类型;p534

12、OutputStream类型;p535

13、装饰器模式;p535

14、InputStream ——>FilterInputStream
——>OutputStream ——>FilterOutputStream
这两个类是装饰器额必要条件;p535

15、FilterInputStream类型;p536
——FilterOutputStream类型;p537

16、Reader和Writer(这两能支持16位unicode字符,老的I/O流集成层次结构只支持8位)

17、自我独立的类:RandomAccessFile;p539

18、I/O流的典型使用方式;p539
——1)缓冲输入文件

public class BufferedInputFile{
    public static String read(String Filename) throw IoException{
        BufferedReader in = new BufferedReader(new FileReader(filename));
        String s;
        StringBuilder sb = new StringBuilder();
        while((s = in.readline) != null){
            sb.append(s + "\n");
        }
        in.close();  // 不要忘记关闭文件流
        return sb.toString();
    }
}

19、从内存输入;p541

20、格式化的内存输入;p541

21、基本的文件输出;p542

public class BasicFileOutput{
    static String file = "BasicFileOutput.out";
    public static void main(string[] args) throws IoException{
        /*输入对象*/
        BufferReader In = new BufferReader(new stringReader(BufferedInputFile.read("BasicFileOutput.java")));
        /*输出对象*/
        PrintWriter out = new PrintWriter(new BufferWriter(new FileWriter(file)));
        int lineCount = 1;
        String s;
        while((s = in.readline()) != null){
            out.printIn(lineCount++ + " : " + s);
        }
        out.close;
        system.out.print(BufferedInputFile.read(file));  // show the store file
    }
}

22、存储和恢复数据(Data…——>Buffer…——>File…)

23、UTF-8简述:utf-8是一种多字节模式,其编码长度根据实际使用的字符集会有变化。

24、对象序列化和XML可能是更容易地存储和读取复杂数据结构的方式。p544

25、读写随机访问文件(RadomAccessFile)p544

26、管道流;p545
——管道流用于任务之间的通信,在多线程中十分有用。

27、读写文件的实用工具。p545

28、标准I/O的意义在于:我们可以很容易的把程序串联起来,一个程序的标准输出可以成为另一个程序的标准输入。

29、按照标准I/O模型,Java提供了System.In,System.Out, System.err;p548

30、新I/O(目的在于提高速度);p551
——速度的提高来自于所使用的结构更接近于操作系统执行I/O的方式:通道和缓冲器。
——我们并没有直接和通道交互;只是和缓冲器交互;并把缓冲器派送到通道。p552

31、缓冲器容纳的是普通的字节,为了把他们转换成字符,我们要么在输入他们的时候对其进行编码(这样输出才有意义),要么将其从缓冲器输出,对他们进行编码。p555

32、对象序列化;p571
——对象能够在程序不运行的情况下仍能存在并保存其信息。
——途径有①信息写入文件②数据库③序列化;p572

33、Java的对象序列化将那些实现serializable接口的对象转换成字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。p572

34、利用序列化可以实现轻量级持久性。

35、transient关键字;p578

36、对象序列化的一个重要限制是它只是Java的解决方案,只有Java程序的才能反序列化这种对象。一种更具交互性的解决方案是将数据转换成为XML格式,这可以使其被各种各样的平台使用。p586

37、第18章总结。p589

第20章

1、注解(也被成为元数据)为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后某个时刻非常方便的使用这些数据。p620

2、通过注解:①我们可以将这些元数据保存在Java源代码中
——————②注解可以用生成描述符文件,或是新的定义,并且有助于减轻“样板”代码的负担。
——————③更加干净易读的代码以及编译期类型减产等。p620

3、Java.lang内置的注解:@override @Deprecated @SuppressWarnings

4、注解的定义看起来很像接口的定义。事实上,与Java其他任务和接口一样,注解也将会编译成class文件。p621
e.g. @Target(ElementType.Method)
——@Rentention(RententionDolicy.Runtime)
——public @interface Test{ }

5、@Target,@Rentention;p621

6、注解的元素看起来就像接口的方法,唯一的区别是你可以为其指定默认值。(没有元素的注解称为标记注解)

7、注解的一个简单应用:跟踪项目用例。p621

—————————–分割线(下面内容为略记)———————————

8、观察者模式;p621
——一个访问者会遍历某个数据结构或一个对象的集合,对其中的每个对象执行一个操作。对每个对象执行的操作,都是基于此对象的类型。

9、第20章总结;p649

再来一篇轻松的
知乎:有哪些「还有这种操作?」的故事?

第21章

1、到目前为止,你学到的都是顺序编程的知识,及程序中的所有事物在任意时候都只能执行一个步骤。p651

2、web系统是最常见的Java应用系统之一,而基本的web库类,Servlet具有天生的多线程性——这很重要,因为web服务器经常包含多个处理器,而并发是充分利用这些处理器的方式。

3、并发通常是提高运行在单处理器上的程序的性能。p651

4、并发能够有效的解决阻塞问题。p651

5、事件驱动编程

6、实现并发最直接的方式就是在操作系统级别使用进程。进程是运行在她自己的地址空的自包容程序。

7、Erlang,一种适合并发编程的语言;p652

8、县城机制是在由执行程序表示的单一进程中创建人物;p652

9、在Java中,通常需要假定不会获得足够的线程,从而使得可以为大型仿真中的每个元素都提供一个线程。

10、协作系统的优势。p653

11、任何时候,你需要保持进程间信息的同步。p653

12、一个线程就是在进程中的单一的顺序控制流,因此,单个进程可以拥有多个并发执行任务。p653

13、定义一个简单的线程任务。p654
——要定义任务,只需要实现Runable接口并编写run( )方法,是的该任务可以执行你的命令。
一个简单的小例子:

public class liftoff implements Runnable{
    protected int countDown = 10;
    private static int taskCount = 0;
    private final int id = taskCount++;
    public liftoff(int countDown){
        this.countDown = countDown;
    }
    public String status() {
        return "#" + id + "c" + (countDown > 0 ? countDown:"liftoff") + ")";
    }
    public void run(){
        While(countDown --> 0){
            System.out.print(status());
            Thread.yield();  //对线程调度器的一种建议,声明:"我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机~~"
        }
    }
}

14、将Runnable对象转变为工作任务的传统方式是把它提交给Thread构造器。下面依旧是一个小例子;p655

public class BasicThreads{
    public static void main(String[] args){
        thread t = new Thread(Liftoff());
        t.start();
        Systerm.out.print("waiting for liftoff...");
    }
}

15、接着去添加更多的线程去驱动更多的任务;p655

public class MoreBasicThreads{
    public static void main(String[] args){
        for(int i=0; i<5; i++){
            new Thread(new liftoff()).start();
        }
        Systerm.out.print("waiting for liftoff...");
    }
}

16、Executor
Java se5的Java.util.concurrent + 包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。p656

17、Runable是执行工作的独立任务,但他不返回任何值。如果你希望任何任务完成时能够返回一个值。那么可以实现callable接口而不是runable接口。p658

18、InterruptedException;p659

19、影响任务行为的一种简单方法是调用sleep( ),这将使任务中止执行给定的时间。p659

20、线程的运行行为依赖于底层的线程机制,在不同的操作系统之间是有差异的。如果你必须控制人物执行的顺序,那么就是用同步控制。p660

21、休眠(sleep( ));p660

22、优先级
——线程的优先级将该线程的重要性传递给了调度器。
——调度器将倾向于让优先权最高的线程执行。
——(优先权不会导致死锁)优先级较低的线程仅仅是执行的频率较低。p660

23、让步(关于yield);p661

24、后台线程;

25、join()方法;p669

26、使用Executor解决捕获异常的问题;p672

27、解决资源竞争的方法是当资源被一个任务使用时,在其上加锁。
——给定时刻只允许一个任务访问共享资源。通常这是通过在代码前面加上一条所语句来实现的。这就使得在一段时间内只有一个任务可以运行这段代码。因为锁语句产生了一种相互排斥的效果。这种机制被称为互斥量。

28、Synchronized,防止资源冲突的内置形式;p677

29、声明Synchronized方法的方式;p677
——Synchronized void f( ){/*……*/}
——Synchronized void g( ){/*……*/}
所有对象都含有单一的锁(也称为监视器)

30、一个任务可以多次获得对象的锁。

31、针对每个里,也有一个锁。p677
——Synchronized static方法可以在累的范围内防止对static数据的并发访问。

32、Brain同步规则;p677

33、显式的lock对象;p678

34、临界区;p685
——有时,你只是希望防止多个线程同时访问方法内部的部分代码而不是访问整个方法。通过这种方式分离出来的代码段被称为临界区。
e.g.

synchronized(SyncObject){
    // xx
    // xx
}

这也被称为同步控制块。

35、线程本地存储;p690
——防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。机制是为使用相同变量的每个不同县城都创建不同的存储。

36、线程可以处于4种状态;p694

37、一个任务进入阻塞状态的原因;p694
——1)调用sleep(milliseconds)使任务进入休眠状态。
——2)调用wait( )使线程挂起,直到得到了notify()或者notifyAll( )消息,线程才会进入就绪状态。
——3)任务在等待某个输入/输出完成。
——4)任务试图在某个对象上调用其同步控制方法,但是对象锁不可用。

38、Thread类包含interruput( )方法。因此你可以终止被阻塞的任务,这个方法将设置线程的中断状态。p695

39、线程间的协作;p702
——当任务协作时,关键问题是这些任务之间的握手。为了实现这种握手,我们使用了相同的基础特性:互斥

40、在互斥之上,我们为任务添加了一种途径,可让其将自身挂起,直至某些外部条件发生变化,表示是时候让这个任务向前开动了为止。p703
使用object方法wait( )和notify( )可以安全实现这种握手。

41、调用sleep( )的时候锁并没有被释放,调用yield( )也属于这种情况。
——当一个任务在方法里遇到了对wait( )的调用的时候,线程的执行被挂起,**对象上的锁被释放。**p703

42、使用while循环包围wait( )用来检查感兴趣的条件。p706

43、线程间通信通过输入/输出通常很有用。p717
——提供线程功能的类库以”管道”的形式对线程间的输入/输出进行了支持。p717

44、死锁:
——死锁的经典例证:哲学家就餐问题。

45、死锁发生的4个必要条件(需同时满足):p721
——1)互斥条件,任务使用中的资源至少有一个不能共享。
——2)至少有一个任务他必须持有一个资源且正在等待获取一个当前被别的任务所持有的资源。
——3)任务不可以抢占资源。
——4)必须有循环等待。(这个比较好破解)

46、免锁容器;p574
——像Vector和hashTable这类早期容器具有许多Synchronized方法,当他们用于非多线程的应用程序中时,便会导致不可接受的开销。p754

47、轻量级的上下文切换(线程)只是改变了程序执行的序列和局部变量。
——进程切换(重量级的上下文切换)必须改变所有的内存空间。p766

48、Erlang:专门用于线程机制的几种函数型语言之一。

49、第21章总结

最后放上仙剑的一首主题曲镇楼~~

这里写图片描述

猜你喜欢

转载自blog.csdn.net/breavo_raw/article/details/75043563