Java核心技术36讲阅读笔记(2)

Java核心技术36讲阅读笔记(2)

本文参考极客时间杨晓峰-Java核心技术36讲
本文为博主阅读《Java核心技术36讲》整理的笔记,如需转载,请附上本文链接

1、Java提供了哪些IO方式,NIO如何实现多路复用?

  • Java IO方式有很多种,基于不同的IO抽象模型和交互方式,可以进行简单区分。
  • 首先,传统的java.io包,它基于流模型实现,提供了我们最熟知的一些IO功能,比如File抽象、输入输出流等。交互方式是同步、阻塞的方式,也就是说,在读取输入楼或者写入输出流时,在读写动作完成之前,线程会一直堵塞在那里,它们之间的调用是可靠的线程顺序。
  • Java.io包的好处是代码比较简单、直观、缺点则是IO效率和扩展性存在局限性 ,容易成为应用性能的瓶颈。
  • 很多时候,人们也把java.net下面提供的部分网络API,比如Socket、ServerSocket、HTTPURLConnection也归类到同步阻塞IO类库,因为网络通信同样是IO行为。
  • 第二,在Java 1.4中引入了NIO框架(java.nio包),提供了Channel、Selector、Buffer等新的抽象,可以构建多路复用的、同步非阻塞IO类库,同时提供了更接近操作系统底层的高性能数据操作方式。
  • 第三,在java 7中,NIO有了进一步的改进,也就是NIO2,引入了异步非阻塞IO方式,也有很多人叫它AIO(Asynchronous IO)。异步操作基于事件和回溯机制,可以简单理解为,应用操作直接返回,而不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后续工作。

2、区分同步或异步

  • 简单来说,同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步;而异步则相反,
  • 其他任务不需要等待当前调用返回,通常依靠事件、回调等机制来实现任务间次序关系。

3、区分阻塞与非阻塞

  • 在进行线程操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如ServerSocket新连接建立完毕,或数据读取、写入操作完成;而非阻塞则是不管IO操作是否结束,直接返回,相应操作在后台继续处理。
  • (不能一概而论认为同步或阻塞就是低效,具体还要看应用和系统特征)

4、IO简单类图

在这里插入图片描述

5、Java有几种文件拷贝的方式?哪一种最高效?

  • Java有多种比较典型的文件拷贝实现方式,比如:
    1. 利用java.io类库,直接为源文件构建一个FileInputStream读取,然后再为目标文件构建一个FileOutputStream,完成写入工作。
    2. 利用java.nio类库提供的transferTo或transferFrom方法实现。
    3. Java标准库本身已经提供了几种Files.copy的实现。
  • 对于Copy的效率,这个其实与操作系统和配置等情况有关,总体上来说,NIO transferTo/From的方式可能更快,因为它更能利用现代操作系统底层机制,避免不必要拷贝和上下文切换。

6、接口和抽象类有什么区别?

  • 接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到API定义和实现分离的目的。接口,不能实例化,不能包含任何非常量成员,任何field都是隐含着public static final的意义,同时,没有非静态方法实现,也就是说要么是抽象方法,要么是静态方法。Java标准类库中,定义了非常多的接口,比如java.util.List。
  • 抽象类是不能实例化的类,用abstract关键字修饰class,其目的主要是代码重用。除了不能实例化,形式上和一般的Java类没有太大区别,可以有一个或者多个抽象方法,也可以没有抽象方法。抽象类大多用于抽取相关类的公用方法实现或者是共同成员变量,然后通过继承的方式达到代码复用的目的。Java标准库中,比如collection框架,很多通用部分就被抽取成为抽象类,例如java.util.AbstractList。
  • Java类实现interface使用implements关键词,继承abstract class则是使用extends关键词。

7、面向对象设计

面向对象的基本要素:封装、继承、多态。

  • 封装的目的是隐藏事物内部的实现细节,以便提高安全性和简化编程。封装提供了合理的边界,避免外部调用者接触到内部的细节。我们在日常开发中,因为无意间暴露了细节导致的难缠bug太多了,比如在多线程环境暴露内部状态,导致并发修改 问题。从另外一个角度看,封装这种隐藏,也提供了简化的界面,避免太多无意义的细节浪费调用者的精力。
  • 继承是代码复用的基础机制,类似于我们对于马、白马、黑马的归纳总结。但要注意,继承可以看作是非常紧耦合的一种关系,父类代码修改,子类行为也会变动。在实践中,过度滥用继承,可能会起到反效果。
  • 多态,你可能立即会想到重写(override) 和重载(overload),向上转型。简单说,重写是父子类中相同名字和参数的方法,不同的实现;重载则是相同名字的方法,但是不同的参数,本质上这些方法签名是不一样的。

面向对象设计的基本原则(S.O.L.I.D原则):

  • ①单一职责(Single Responsibility):类或者对象最好是只有单一职责,在程序设计中如果发现某个类承担着多种义务,可以考虑进行拆分。
  • ②开关原则(Open-Close,Open for extension,Close for modification):设计要对扩展开放,对修改关闭。换句话说,程序设计应保证平滑的扩展性,尽量避免因为新增同类功能而修改已有实现,这样可以少产生出些回归(regression)问题。
  • ③里氏替换(Liskov Substitution):这是面向对象设计的基本要素之一,进行继承关系抽象时,凡是可以用父类或者基类的地方,都可以用子类替换。
  • ④接口分离(Interface Segregation):我们在进行类和接口设计时,如果在一个接口里定义了太对方法,其子类很可能面临两难,就是只有部分方法对它是有意义的,这就破坏了程序的内聚性。对于这种情况,可以通过拆分成功能单一的多个接口,将行为进行解耦。在未来维护中,如果某个接口设计有变,不会对使用其他接口的子类构成影响。
  • ⑤依赖反转(Dependency Inversion):实体应依赖于抽象而不是实现。也就是说高层次模块不应该依赖于低层次模块,而是应该基于抽象。实践这一原则是保证产品代码之间适当耦合度的法宝。

8、设计模式

大致按照设计模式的应用目标分类,设计模式可以分为创建型设计模式、结构型设计模式和行为型设计模式。

  • 创建型模式,是对对象创建过程中的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构造器模式(Builder)、原型模式(ProtoType)。
  • 结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式包括桥接模式(Bridge)、适配器模式(Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、外观模式(Facade)、享元模式(ProtoType)等。
  • 行为型模式,是对类或者对象之间交互、职责划分等角度总结的模式。比较常见的行为型模式有策略模式(Strategy)、解释器模式(Interpreter)、命令模式(Command)、观察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(Template Method)、访问者模式(Visitor)。

9、synchronized和ReentrantLock有什么区别?synchronized最慢这句话正不正确?

  • synchronized是Java内建的同步机制,所以也有人称其为Intrinsic Locking,它提供了互斥的语义和可见性,当一个线程已经获取当前锁时,其他试图获取的线程只能等待或者阻塞在那里。
  • 在Java 5以前, synchronized是仅有的同步手段,在代码中, synchronized可以用来修饰方法,也可以使用在特定的代码块儿上,本质上synchronized方法等同于把方法全部语句用synchronized块包起来。
  • ReentrantLock,通常翻译为再入锁,是Java 5提供的锁实现,它的语义和synchronized基本相同。再入锁通过代码直接调用lock()方法获取,代码书写也更加灵活。与此同时, ReentrantLock提供了很多实用的方法,能够实现很多synchronized无法做到的细节控制,比如可以控制fairness,也就是公平性,或者利用定义条件等。但是,编码中也需要注意,必须要明确调用unlock()方法释放,不然就会一直持有该锁。
  • synchronized和ReentrantLock的性能不能一概而论,早期版本synchronized在很多场景下性能相差较大,在后续版本进行了较多改进,在低竞争场景中表现可能优于ReentrantLock。

10、保证线程安全的两个方法:

  • 封装:通过封装,我们可以将对象内部状态隐藏、保护起来。
  • 不可变:Java语言目前还没有真正意义上的原生不可变、但是未来也许会引入。

11、线程安全小保证几个基本特征:

  • 原子性:简单说就是相关操作不会中途被其他线程干扰,一般需要同步机制实现。
  • 可见性:是一个线程修改了某个共享变量,其状态能够立即被其他线程知晓,通常被解释为将线程本地状态反映到主内存上,volatile就是保证可见性的。
  • 有序性:是保证线程内串行语义,避免指令重排等。

12、synchronized和ReentrantLock的比较

  • 1 用法比较

    • Lock使用起来比较灵活,但是必须有释放锁的配合动作
    • Lock必须手动获取与释放锁,而synchronized不需要手动释放和开启锁
    • Lock只适用于代码块锁,而synchronized可用于修饰方法、代码块等
  • 2 特性比较

    • ReentrantLock的优势体现在:
    • 具备尝试非阻塞地获取锁的特性:当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁
    • 能被中断地获取锁的特性:与synchronized不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
    • 超时获取锁的特性:在指定的时间范围内获取锁;如果截止时间到了仍然无法获取锁,则返回
  • 3 注意事项

    • 在使用ReentrantLock类的时,一定要注意三点:
    • 在fnally中释放锁,目的是保证在获取锁之后,最终能够被释放
    • 不要将获取锁的过程写在try块内,因为如果在获取锁时发生了异常,异常抛出的同时,也会导致锁无故被释放。
    • ReentrantLock提供了一个newCondition的方法,以便用户在同一锁的情况下可以根据不同的情况执行等待或唤醒的动作。
发布了37 篇原创文章 · 获赞 75 · 访问量 2574

猜你喜欢

转载自blog.csdn.net/qq_43472474/article/details/104544308