《Java程序员修炼之道》
|
路线图
|
——并发,性能,字节码,类加载
——Java未来发展:lambda表达式,模块化Jigsaw项目。
|
初识Java7
|
Java7的变化为两块:Coin项目,NIO.2。
1.1 Java语言和平台的区别
Java系统规范中最重要的是:《Java语言规范》JLR和《JVM规范》VMSpec。
连接Java语言平台之间的是:统一的类文件.class格式定义。
大多数流行框架会在类加载过程中对类进行改造。
流程:.java(javac).class(类加载器)转换后的.class(解释器)可执行代码(JIT编译器)机器码。
注意:JVM字节码实际上只是一种中间语言,不是真正的机器码。
而所谓的编译器javac也不同于gcc,它只是一个针对Java源码生成类文件的工具。
Java体系中真正的编译器是JIT。
1.2 Coin项目:浓缩的都是精华
硬币一样小的变化。
提供新功能(难度递增):1.类库 2.工具提供的功能 3.语法糖4.语言新特性 5.类文件格式的变化 6.VM的新特性
——语法糖:数字中的下划线(Java7)
——新的语言小特性,try-with-resources(Java7)
——类文件格式的变化,注解(Java5)
——JVM的新特性,动态调用(Java7)
Coin项目:3.语法糖4.语言新特性。
1.3 Coin项目中的修改
语法的6个新特性:
1.switch语句中的String。
2.数字常量的新形式。
3.改进的异常处理Multi-catch。
4.try-with-resources结构,自动关闭资源。
5.砖石语法:处理泛型。
6.变参警告位置的修改
1.3.1 switch语句中的String
支持byte,char,short,int,枚举常量和String
1.3.2 更强的数值文本表示法
—— 数字常量可用二进制文本表示
—— 整形常量中可使用下划线
以前:int x = Integer.parseInt("1100110",2);
Java7:int x = 0b1100110 (与二进制打交道)
100_00_000:编译器会在编译时去掉下划线
long anotherLong = 2_147_483-648L;
1.3.3 改善后的异常处理
multicatch与final重抛。
try{....} catch(FileNotFoundException | ParseExceptoin e) {.....}
catch(final Exception e ) {......}
final重抛:表明实际抛出的异常就是运行时遇到的异常。避免抛出笼统的异常类型。
1.3.4 try-with-resourch(TWR)
离开代码块时,资源自动关闭。文件或类似的东西。
把资源放在try的圆括号里:
try( OutputStream out = new FileOutputStream(file);
InputStream is = url.openStream() ) {
byte[] buf = new byte[4096];
int len;
while((len = is.read(buf))>0){
out.write(buf,0,len);
}
}
资源自动化管理代码块的基本形式
正确的用法:为各个资源声明独立变量
try(FileInputStream in = new FileInputStream("SomeFile.bin");
ObjectInputStream in = new ObjectInputStream(fin) ){......}
Java7中大多数资源类都被修改过,实现AutoCloseable接口。
1.3.5 砖石语法
针对创建泛型定义和实例太多繁琐的问题
原来:
Map<Integer,Map<String,String>> usersLists =
new HashMap<Integer,Map<String,String>>();
类型推断改进:
Map<Integer,Map<String,String>> usersLists =
new HashMap<>();
1.3.6 简化变参方法调用
远离类型系统
修改语言非常困难,用类库实现新特性总是相对容易一些。
第2章 新I/O
本章内容:
——新I/O API(NIO.2)
——Path:基于文件和目录的I/O新基础
——Files应用类和各种辅助方法
——如何实现常见的I/O应用场景
——介绍异步I/O
核心类库的变化,java.nio包内。掌握Java之前版本处理I/O的方法。
优点:
——完全取代java.io.File与文件系统的交互。
——新的异步处理类,无需手动配置线程池和其他底层并发控制。便可在后台线程中执行文件和网络I/O操作。
——引入新的Network-Channel构造方法,简化了套接字与通道的编码工作。
Java7的NIO.2 API支持目录树导航(Files.walkFileTree()),符号链接Files.isSysbolicLink(),能用一行代码读取文件属性(Files.readFileAttributes())
读取这些文件很可能打断程序的主流程。面对这一要求,在Java5/6时代可能会用java.util.concurrent中的类创建线程池和工作线程队列,再用单独后台线程读取文件。
NIO.2 API 中用AsynchronousFileChannel,不用指定工作线程或队列就可在后台读取大型文件。
NIO2为多线程文件和套接字访问的应用提供了一个简单的抽象层。IDE,应用服务器和各种流行的框架会大量应用这些特性。
将try-with-resources和NIO.2中的新API结合起来可以写出非常安全的I/O程序。
先了解新的文件系统抽象层:Path和它的辅助类。Path之上,常用的文件系统操作,复制和移动文件。
|
2.1 Java I/O简史
|
2.1.1 Java1.0-1.3
缺乏对非阻塞I/O的支持。
2.1.2 在Java1.4中引入NIO
——在Java1.4中引入非阻塞I/O
——在Java7中对非阻塞I/O进行修改
2002年发布Java1.4时新增的特性:
——为I/O操作抽象出缓冲区和通道层。
——字符集的编码和解码功能。
——提供了能够将文件映射为内存数据的接口。
——实现非阻塞I/O的能力。
——基于流行的Perl实现的正则表达式类库。
但那时对文件系统中的文件和目录处理而言支持力度还不够。那时的java.io.File类有些比较烦人的局限性。
——在不同的平台中对文件名的处理不一致。
——没有统一的文件属性模型(比如读写访问模型)
——遍历目录困难。
——不能使用平台/操作系统的特性
——不支持文件系统的非阻塞操作。
2.1.3 下一代I/O - NIO.2
NIO.2的三个主要目标:
(1)提供一个批量获取文件属性的文件系统接口。去掉和特定文件系统相关的API,还有一个用于引入标准文件系统实现的服务提供者接口。
(2)提供一个套接字和文件都能够进行异步(与轮询、非阻塞相对)I/O操作的API。
(3)完成JSR-51中定义的套接字——通道功能,包括额外对绑定,选项配置和多播数据报的支持。
|
2.2 文件I/O的基石:Path
|
NIO.2把位置(Path表示)的概念,和物理文件系统的处理(复制一个文件)分得很清楚。
2.2.1 创建一个Path
Paths.get(String first, String...more)
Path listing = Path.get("/usr/bin/zip")
listing.toAbsolutePath()
2.2.2 从Path中获取信息
正在处理的路径的相关信息。
listing.getFileName()
listing.getNameCount()获取名称元素的数量
listing.getParent()
listing.getRoot()
listing.sunpath(0,2)
2.2.3 移除冗余项
Path normalizedPath = Paths.get("./Listing_2_1.java").normalize();
toRealPath()方法融合了toAbsolutePath()和normalize()两个方法,还能检测并跟随符号链接。
2.2.5 NIO.2 Path和Java已有的File类
File file = new File("../Listing_2_1.java");
Path listing = file.toPath();
listing.toAbsolutePath();
file = listing.toFile();
|
2.3 处理目录和目录树
|
2.3.1 在目录中查找文件
过滤流中使用到的模式匹配称为glob模式匹配。
2.3.2 遍历目录树
Files.walkFileTree(Path startingDir, FileVisitor<? super Path> visitor);
默认实现类:SimpleFileVisitor<T>
|
public class Find
{
public static void main(String[] args) throws IOException
{
Path startingDir = Paths.get("C:\\workspace\\java7developer\\src");
Files.walkFileTree(startingDir,new FindJavaVisitor());
}
private static class FindJavaVisitor extends SimpleFileVisitor<Path>
{
@Override
public FindVisitorResult visitFile(Path file,BasicFileAttributes attrs)
{
if(file.toString().endsWith(".java")){
System.out.println(file.getFileName());
}
return FindVisitorResult.CONTINUE;
}
}
}
|
|
2.4 NIO.2的文件系统I/O
|
Files类(复制移动删除或者处理文件的工具类),WatchService类(监视文件或目录的核心类)。
Path target = Paths.get("D:\\backup\\lisuxuan.txt");
2.4.2 文件的复制和移动
Path source = Path.get("...");
Files.copy(source,target,REPLACE_EXISTING);
Files.move(Path source,Path target,CopyOptions...);
2.4.3 文件的属性
Files.getLastModifiedTime(zip);
Files.size(zip);
Files.readAttributes(zip,"*");
2.4.4 快速读取文件
1.打开文件
用带缓冲区的读取器和写入器或者输入输出流。
Path logFile = Paths.get("/tmp/app.log");
try(BufferedReader reader = Files.newBufferedReader(logFile,StandardCharsets.UTF_8)){
String line;
while( (line = reader.readline())!= null ){
...
}
}
打开写入文件
Path logFile = Paths.get("/tmp/app.log");
try(BufferedWriter writer= Files.newBufferedWriter(logFile,StandardCharsets.UTF_8,StandardOpenOption.WRITE)){
Writer.write("hello world!");
...
}
}
设置字符编码 new String(byte[],StandardCharsets.UTF_8)
兼容过去基于java.io包的I/O:
Files.newInputStream(Path,OpenOption...)
读取全部行和全部字节的简化方法:
Path logFile = Paths.get("/tmp/app.log");
List<String> lines = Files.readAllLines(logFile,StandardCharsets.UTF_8);
byte[] bytes = Files.readAllBytes(logFile);
2.4.5 文件修改通知
2.4.6 SeekableByteChannel
java.nio.channels.FileChannel
这个类的寻址能力让开发人员可以灵活的处理文件内容。
Path logFile = Paths.get("c:\\temp.log");
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileChannel channel = FileChannel.open(logFile,StandardOpenOption.READ);
channel.read(buffer,channel.size() - 1000);
|
2.5 异步I/O 操作
|
作用:可以使用多个后台线程读写文件、套接字和通道中的数据。
AsynchronousFileChannel
AsynchronousSocketChannel
AsynchronousServerSocketChannel
2.5.1 将来式
使用java.util.concurrent.Future接口。
当你希望由主控线程发起I/O操作,并轮询等待结果时,一般都会采用将来式异步处理。
Future:用来保存异步处理的操作结果。
实现:API/JVM为执行这个任务创建了线程池和通道组。
AsynchronousFileChannel会关联线程池,它的任务是接收I/O处理事件,并分发给负责处理通道中I/O操作结果的结果处理器。跟通道中发起的I/O操作关联的结果处理器确保是由线程池中的某个线程产生的。
默认线程池是由AsynchronousChannelGroup类定义的系统属性进行配置的。
2.5.2 回调式
主线程会派一个侦察员CompletionHandler到独立的线程中执行I/O操作。这个侦查员将带着I/O操作的结果返回到主线程中,这个结果会触发自己的completed或failed方法。
在异步事件刚一成功或者失败并需要马上采取行动的时候,采用回调式。
代码清单2-9 异步I/O——回调式
|
============================================
try
{
Path file = Paths.get("/usr/karianna/foobar.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
ByteBuffer buffer = ByteBuffer.allocate(100_000);
channel.read(buffer,0,buffer,new CompletionHandler<Integer,ByteBuffer>()
{
public void completed(Integer result,ByteBuffer attachment)
{
System.out.println("Bytes read [" + result + "]");
}
public void failed(Throwable exception,ByteBuffer attachment)
{
System.out.println(exception.getMessage());
}
});
}catch(IOException e)
{
System.out.println(e.getMessage());
}
|
|
2.6 Socket和Channel的整合
|
NetworkChannel
2.6.1 NetworkChannel
新接口java.nio.channels.NetworkChannel代表一个连接到网络套接字通道的映射。
|
package com.java7developer.chapter2;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketOption;
import java.net.StandardSocketOptions;
import java.nio.channels.NetworkChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;
public class Listing_2_10 {
public static void main(String[] args) {
SelectorProvider provider = SelectorProvider.provider();
try {
NetworkChannel socketChannel = provider.openSocketChannel();
SocketAddress address = new InetSocketAddress(3080);
socketChannel = socketChannel.bind(address);
Set<SocketOption<?>> socketOptions = socketChannel.supportedOptions();
System.out.println(socketOptions.toString());
socketChannel.setOption(StandardSocketOptions.IP_TOS, 3);
Boolean keepAlive = socketChannel
.getOption(StandardSocketOptions.SO_KEEPALIVE);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
|
2.6.2 MuticastChannel
|
package com.java7developer.chapter2;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.channels.DatagramChannel;
import java.nio.channels.MembershipKey;
public class Listing_2_11 {
public static void main(String[] args) {
try {//选择网络接口
NetworkInterface networkInterface = NetworkInterface.getByName("net1");
DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET);
dc.setOption(StandardSocketOptions.SO_REUSEADDR, true);
dc.bind(new InetSocketAddress(8080));
dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
InetAddress group = InetAddress.getByName("180.90.4.12");
MembershipKey key = dc.join(group, networkInterface);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
|
|
第二部分 关键技术
|
——依赖注入。
|
第3章 依赖注入
3.1 理解IoC和DI
|
依赖注入:使得对象从别人得到依赖项,而不是由它自己来构造。
|
package com.java7developer.chapter3;
import java.util.ArrayList;
import java.util.List;
/**
* Code for listing 3_4
*/
public class HollywoodServiceWithDI {
public static List<Agent> getFriendlyAgents(AgentFinder finder) {
List<Agent> agents = finder.findAllAgents();
List<Agent> friendlyAgents = filterAgents(agents, "Java Developers");
return friendlyAgents;
}
public static List<Agent> filterAgents(List<Agent> agents, String agentType) {
List<Agent> filteredAgents = new ArrayList<>();
for (Agent agent : agents) {
if (agent.getType().equals("Java Developers")) {
filteredAgents.add(agent);
}
}
return filteredAgents;
}
}
|
|
3.2 Java中标准的DI
|
2009年Guice和SpringSource宣布合作,共同打造一组标准的接口注解。JSR-330规范请求。倡导JavaSE的DI标准化。(javax.inject)
|
3.3 Java中的DI参考实现:Guice 3
|
|
4.1 并发理论简介
|
4.1.1 解释Java线程模型
|
4.2 块结构并发(Java5之前)
|
synchroized,volatile等并发关键字,原始的、低级的多线程编程方式。
4.2.1 同步与锁
|
4.3 现代并发应用程序的构件
|
2006 Java5关键技术
1.依赖注入:解耦,可测试性,易读性
2.并发编程:多核CPU革命
java.util.concurrent内存模型,线程与并发实现
3.类加载
JVM如何加载,链接和验证类。用Javap深入字节码。
4.反射:Reflection
Java7的MethodHandle,MethodType和动态调用。
5.性能调优艺术
如何评测
6.垃圾回收GC和即时JIT编译器
JVM中能影响性能的两个主要部分
Java和JVM的内部工作机制
《Java程序员修炼之道》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- Servlet容器的启动(Tomcat为例)
一.容器简介 在tomcat容器等级中,context容器直接管理servlet在容器中的包装类Wrapper,所以Context容器如何运行将直接影响servlet的工作方式. tomcat容器模型 ...
- OpenGL学习之路(二)
1 引子 在上一篇读书笔记中,我们对书本中给出的例子进行详细的分析.首先是搭出一个框架:然后填充初始化函数,在初始化函数中向OpenGL提供顶点信息(缓冲区对象)和顶点属性信息(顶点数组对象),并启用 ...
- Android MVPR 架构模式
最近我在尝试让 Google 的 IO App 变得可单元测试,我这样做的其中一个原因是验证 Freeman 和 Pryce 在引用中对单元测试的总结.即使现在我还是没有把 IOSched 中的任何一 ...
- ssh 或者 scp 无需输入密码的解决办法
这里假设主机A(192.168.100.3)用来获到主机B(192.168.100.4)的文件. 在主机A上执行如下命令来生成配对密钥: ssh-keygen -t rsa 遇到提示回车默认即 ...
- Android应用性能优化之使用SQLiteStatement优化SQLite操作
平常在做Android数据库操作时,都是用的execSQL之个方法. 今天偶然发现了SQLiteStatement这个类.让我想起了在做Java Web开发写JDBC的代码时Prestatement这 ...
- Linux获取时间日期方法
linux中用shell获取昨天.明天或多天前的日期:在Linux中对man date -d 参数说的比较模糊,以下举例进一步说明:# -d, --date=STRING display time d ...
- Web Developer可以做得更多
美国雅虎前端工程师Hedger Wang.这位原雅虎奇摩的第一位Web Developer,非常慷慨的与我们分享了他丰富的经验.现身说法,比空洞的理论更有感染力,我们发现现在遇到的很多问题也都是他曾经 ...
- SDE+ORACLE优化配置
原文 SDE+ORACLE优化配置 SDE的性能取决于: 首先操作系统的性能:其次是Oracle的性能,再次是SDE的性能. 第一:操作系统,无非是内存.CPU.带宽等. 可以有待提高的地方:第一.硬 ...
- 9、NFC技术:NDEF文本格式解析
NDEF文本格式规范 不管什么格式的数据本质上都是由一些字节组成的.对于NDEF文本格式来说.这些数据的第1个字节描述了数据的状态,然后若干个字节描述文本的语言编码,最后剩余字节表示文本数据. ...
- location.hash来保持页面状态
/*本例是为了在客户端页面返回时保存状态,采用hash值记录的模式,为了使用方便所写的存取hash值的库,时间仓促,望指出错误.*/var pageStateHash = { hashArray: [ ...