额外
- 窗体程序外加EventQueue…invokeLater()的原因:
awt和swing是单线程模式的, 所有aawt组件只能在事件处理线程中访问, 从而保证组件状态的可确定性
第一章 Java SE8 的流库
1.1 从迭代到流的操作
- 流操作的基本使用
List<String> words = ... words.stream.stream().filter(w -> w.length() > 12).count; words.parallelStream.stream().filter(w -> w.length() > 12).count; // 并行方式
string.split("\\PL+")
将字符串以非字母分割
1.2 流的创建
- 数组->流
Stream<String> words = Stream.of(str); // str是String数组 Stream<String> words1 = Array.stream(str, 0, 5); // str是String数组
- 生成无限流
Stream<String> echos = Stream.generate(() -> "Echo"); Steam<Double> randoms = Stream.generate(Math::random); Stream<BigInteger> integers = Stream.iterate(BigIteger.ZERO, n -> n.add(BigInteger.ONE)); // 产生无限序列流: 0 1 2 3 4
1.3 filter、map和flatMap方法
- 若使用map会得到包含流的流, 则可以使用flatMap方法将其摊平, 而不是使用Map
1.4 抽取子流和连接流
stream.limit(n)
会返回一个新的流, 在n个元素之后结束stream.skp(n)
会丢弃前n个元素- Stream类的静态方法
concat(Stream stream1, Stream stream2)
可以将两个流连接起来
1.5 其他流转换
stream.distinct()
会按顺序剔除重复元素stream.sorted()
可以操作Coparable元素的流或接受一个Comparatorpeek
方法每次获取一个元素时, 都会调用一个函数
1.6 简单约简
- count、max、min、findFirst、findAny、anyMatch、allMatch、noneMatch
1.7 Optional类型
- 包装器对象
Optional<T>
主要函数orElse()
、orElseGet()
、orElseThrow()
1.8 收集结果
- 流可以调用forEach方法, 将某个函数应用与每个元素, 如
stream.forEach(System.out::println);
stream.toArray()
返回的是Object[]数组, 如果想让数组具有正确的类型, 应将其传入构造器中stream.toArray(String[]new);
- 流的元素收集到List中, 可以使用
stream.collect(Collectors.toList());
(Set同理) - 流的元素收集到String中, 可以使用
stream.collect(Collectors.joining());
1.9 收集到映射表中
Collectors.toMap
方法
1.10 群组和分区
- 根据函数进行分类
Map<String, List<Locale>> countryToLocales = locales.collect(Collectors.groupingBy(Locale::getCountry));
- 当分类函数的返回类型是boolean值是, 分区为两类, 使用partitioningBy比使用groupingBy更高效
Map<Boolean, List<Locale>> englishAndOtherLocales = locales.collect( Collectors.partitioningBy(l -> l.getLanguage().equals("en"))); List<locale> englishLocales = englishAndOtherLocales.get(true);
1.11 下游收集器
groupingBy
可以将各个集组合起来, 但是可能会产生复杂的表达式- 示例:
cities = readCities("/home/zyq/文档/cities.txt"); Map<String, Optional<String>> stateToLongestCityName = cities.collect( groupingBy( City::getState, mapping(City::getName, maxBy(Comparator.comparing(String::length))) ) ); System.out.println("stateToLongestCityName: " + stateToLongestCityName);
1.12 约简操作
- reduce方法是从流中计算某个值的通用机制, 最简单形式接受一个二院函数, 并从前两个元素开始持续应用它. 也可以在将第一个参数设置成计算起点
- 复杂示例, 计算字符串流中所有字符穿的长度和, 其中
(total, word) -> total + word.length()
是累积器, 会被重复调用int result = words.reduce(0, (total, word) -> total + word.length(), (total1, total2) -> total1 + total2);
1.13 基本类型流
- 创建基本类型流
IntStream stream = IntStream.of(..)
、IntStream stream = Arrays.stream()
, 和对象流一样, 可以使用generate和iterate方法 - 还可以使用IntStream和LongStream类的静态方法range和rangeClosed生成步长为1的证书范围
- 将对象流转换为基本类型流可以使用
stream.mapToInt
以及stream.mapToLong
等
1.14 并行流
- 不要修改在执行某项流操作后会将元素返回到流中的集合
第二章 输入与输出
2.1 输入/输出流
- java中如缓冲和预览等细节需要将多个流过滤器组合起来使用, 提供极大的灵活性
// 例: 从一个ZIP压缩文件中通过输入流序列来读入数字 ZipInputStream zin = new ZipInputStream(new FileInputStream("employee.zip")); DataInputStream din = new DataInputStream(zin);
2.2 文本输入与输出
- 文本输出可以使用
PrintWriter
. 输出可以使用print、println、printf方法. 构造函数第三个参数为Boolean类型, 用于设置是否自动冲刷, 若设置成自动冲刷模式, 那么只要println被调用, 缓冲区中的所有字符都会被发送PrintWriter out = new PrintWriter("employee.txt", "UTF-8"); // 等同于 PrintWriter out = new PritWriter(new FileOutputStream("employee.txt"), "UTF-8");
- 文本输入的几种方式
// 1. Scanner类对象 // 2. 对短小文本文件 String content = newString(File.readAllByBytes(path), charset); // 3. 逐行读入 List<String> lines = Files.readAllLines(Path, charset) // 4. 文件太大, 将行惰性处理 try (Stream<String> lines = Files.lines(path, charset)) { ...... }
2.3 读写二进制数据
- java中所有的值都按照高位在前的模式写出
- 写出: 实现了DataOutput接口(writeChars等)的DataOutputStream类; 读入: 实现了DataInput接口(readInt等)的DataInputStream类
- 随机访问文件类, RandomAccessFile, 同时实现了DataInput和DataOutput接口, 因此可以使用readInt/writeInt等方法, 使用seek()
RandomAccessFile in = new RandomAccessFile("employee.dat", "r"); RandomAccessFile inOut = new RandomAccessFile("employee.dat", "rw");
2.4 对象输入/输出流与序列化(疑惑)
- 使用的对象需要实现Serializable接口
- 对象深克隆的两种方法:
①对象类实现Clonable接口, 重写clone()函数
②对象类实现Cloneable和Serializable接口, 实现clone()函数, 在函数中将对象序列化到输出流中, 再将其读回, 使用ByteArrayOutputStream将数据保存到字节数组中
2.5 操作文件
- Path生成路径后可以使用
p.resolve(Path q)
、p.resolveSibling()
、p.getParent()
等方法对路径进行操作 - 小文件的读写使用Files类的静态函数, 大文件或二进制文件使用输入输出流更合适
- Files类的静态方法:
createDirectory(Path path)
、createDirectories(Path path)
、createFile(Path path)
- Files类的copy和move函数可以复制和移动文件, delete和deleteIfExists可以删除文件, 使用StandCopyOption的静态变量作为参数可以设置其为原子操作或覆盖原文件等等
- 静态的
Files.list
方法返回可以读取目录中各个项的Stream<Path>
对象, 因为是惰性读取, 因此使得处理具有大量项的目录变得高效.Files.list
方法不会进入子目录, 为了处理所有子目录, 应该使用Files.walk
- 遍历目录中想进行细粒度的控制可以使用
Files.newDirectoryStream(dir)
方法, 返回一个DirectoryStream<Path>
对象. 访问目录的所有子孙成员, 可以调用
同时选择性覆盖其中的操作方法File.walkFileTree(Paths.get(path), new SimpleFileVisitor<Path> { ... });
2.6 内存映射文件(疑惑)
- 文件锁是依赖于操作系统的, 同时需要确保在操作完成时释放锁, 因此最好在try中执行
共享锁:FileChannel = FileChannel.open(path); FileLock lock = channel.lock(); // 无锁时阻塞 FileLock lock = channel.trylock(); // 无锁时返回null
FileLock lock(long position, long size, boolean shared)
, shared为true则允许多个进程从文件读入
2.7 正则表达式
- X+: 1个或多个. X*: 0个或多个. X?: 0个或1个
- 正则表达式的使用
编译时可以使用第二个参数设置一个或多个标志Pattern pattern = Pattern.compile(patternString); Matcher matcher = pattern.matcher(input); if (matcher.matches())...
Pattern pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE + Pattern.UNICODE_CASE);
- 在集合或流中匹配
Stream<string> result = strings.filter(pattern.asPredictt());
第三章 XML
3.1 XML概述
- 属性只应该用来修改值的解释, 而不是用来指定值
3.2 解析XML文档
- 读入XML文档需要DocumentBuilder对象, 可以从工厂类DocumentBuilderFactory中得到
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = facetory.newDocumentBuilder(); File f = ...; Document doc = builder.parse(f); URL u = ....; doc = builder.parse(u);
- 通过
doc.getDocumentElement()
方法获得Element
元素(根元素). 通过element.getTagName()
方法获得元素的标签名. 通过element.getChildNodes()
方法获得子元素, 返回NodeList
集合, 获得的子元素有可能包含元素标签之间的空白字符 string.trim()
方法会把位于实际数据前后的空白字符删掉element.getFirstChild()
获取第一项子元素,element.getNextSibling
得到下一个兄弟节点,getLastChild
获取最后一项子元素element.getAttributes()
方法返回NamedNodeMap对象, 包含了描述属性的Node对象, 然后调用getNodeName
和getNodeValue
方法可以得到属性名和属性值. 如果知道属性名也可以直接使用element.getAttribute(String attrName)
方法获得相应属性值
第四章 网络
4.1 连接到服务器
- 简单示例
Socket s = new SOkcet("time-a.nist.gov", 13); // 打开套接字 InputStream inStream = s.getInputStream();
- 套接字超时, 超时后会抛出SocketTimeoutException异常
Socket s = new Socket(); s.connect(new InetSocketAddress(host, port), timeout);
- 根据主机名获得主机地址
InetAddress address = InetAddress.getByName("time-a.nist.gov"); byte[] addressBytes = address.getAddress(); // 若一个主机名对应多个主机地址 InetAddress[] address = InetAddress.getAllByName(host); // 获取本机地址 InetAddress address = InetAddress.getLocalHost();
4.2 实现服务器
- ①开启ServerSocket监听端口②有客户端连接时调用accept返回Socket③利用
socket.getInputStream()
和socket.getOutputStream()
获取输入流和输出流 - 应采用线程处理类实现
Runnable
接口, 实现多线程while (true) { Socket incoming = s. accept(); Runnable r = new ThreadedEchoHandler(incoming); Thread t = new Thread(r); t.start(); }
- 半关闭,
socket.shutdownOutput();
关闭输出流, 保持输入流打开 - 可中断套接字使用SocketChannel类, 当线程正在执行打开、读取或写入操作时, 如果线程发生终端, 那么这些操作将不会陷入阻塞, 而是以抛出异常的方式结束
SocketChannel Channel = SocketChannel.open(new InetSocketAddress(host, port)); Scanner in = new Scanner(channel, "UTF-8"); OutputStream outStream = Channels.newOutputStream(channel);
4.4 获取Web数
- URI类可以解析相对URL以及相对化URI
- 使用URLConnection获取信息
URL url = new URL(urlString); URLConnection connection = url.openConnection(); // 获得头字段, 还可以使用connection.getContentType()等 Map<String, List<String>> headers = connection.getHeaderFields(); ...
第五章 数据库编程
5.3 JDBC配置
- 打开数据库连接
String url = "jdbc:xxxx"; String username = "xxxx"; String pasword = "xxxx"; Connection conn = Drivermanager.getConnection(url, username, password);
5.4 使用JDBC语句
- INSERT、UPDATE、DELETE、CREATE TABLE、DROP TABLE等操作可以使用
executeUpdate
方法Statement state = conn.createStatement(); String command = "UPDATE ... SET... WHERE..."; state.executeUpdate(command);
- SELECT操作执行
executeQuery
方法, 返回ResultSet对象ResultSet rs = state.executeQuery("SELECT * FROM BOOK"); while (rs.next()) { ... }
5.5 执行查询操作
- 执行操作返回多个结果集时可以使用boolean对象接收执行结果, 如果多结果集下一项是结果集, 将返回true, 如果下一项不是更新计数, 将返回-1
第六章 日期和时间API
6.1 时间线
Instant.now()
函数返回当前时刻Instant
类对象.Douration.between(Instant start, Instant end)
函数返回时间差Duration
对象.Duration
对象调用toNanos
、toMillis
、getSerconds
、toMinutes
、toHours
、toDays
来获得传统度量单位的时间长度
6.2 本地时间
LocalDate.now()
、LocalDate.of(int year, int month, int day)
返回本地时间类对象plusDays
、plusMonths
、minusDays
、minusMonths
、plus
、minus
(减去Duration或Period)、withDayOfMont
(返回新的LocalDate)、getDayOfMonth
、until
(获取Period, 或按照给定的ChronoUnit是计算的数值)、isBefore
6.3 日期调整器
- TemporalAdjusters接口实现的几个方法, 可以将其返回值传递给LocalDate对象的with函数, 返回一个新的LocalDate对象. 也可以自己实现TemporalAdjuster接口来创建自己的调整器
6.4 本地时间
- 存储固定时区的时间点使用LocalDateTime类
6.5 时区时间
- 如果计算需跨夏令时或需要处理不同时区的情况, 应该使用ZonedDateTime类
- 构造器
LocalDateTime ldt = LocalDateTime.of(1990, 12, 30, 2, 30, 0, ZoneId.of("American/New_York")); ldt = LocalDateTime.of(LocalDate,of(1990, 12, 30), LocalTime.of(2, 30), ZoneId.of("American.New_York"));
6.6 格式化和解析
- 使用预定义的格式器
String formatted = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(apollo1launch);
-
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); String formatted = formatter.format(apollo11launch); formatted = formatter.withLocale(Locale.FRENCH).format(apollo11launch);
- 自定义格式
formatter = DateTImeFormatter.ofPattern("E yyyy-MM-dd HH:mm");
- 解析
LocalDate churchsBirthday = LocalDate,parse("1903-06-14"); ZonedDateTime apollo11launch = ZonedDateTime.parse("1969-07-16 03:32:00-0400", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxx"));
第七章 国际化
7.1 Locale对象
- 示例
Locale usEnglish = Locale.forLanguageTag("en-US"); Locale loc = new Locale("de", "CH"); loc = Locale.GERMAN;
7.2 数字格式
- 数字和货币的格式高度依赖于locale
- NumberFormat类的静态方法返回相应的数字\货币量\百分比(NumberFormat类对象)格式器, 可以对相应的数据进行格式化和解析
7.3 货币
- 更改货币格式化器
NumberFormat euroFormatter = NumberFormat.getCurrencyInstatnce(Locale.US); euroFormatter.setCurrency(Currency.getInstance("EUR"));
7.4 日期和时间
- FormatStyle设置格式化风格
FormatStyle style = FormatStyle.SHORT; // MEDIUM/LONG/FULL DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(style).withLocale(locale)
7.5 排序和范化
- 因为Collator类实现了Comparator接口, 因此可以将Collator类对象给list.sort(Comparator)方法来进行排序
Collator coll = Collator.getInstance(locale); words.sort(coll);
- 字符串范化形式存储
String name = ...; String normalized = Normalizer.normalize(name, Normalizer.Form.NFD);
7.6 消息格式化
- 示例
String msg = MessageFormat.format("On {2, data, long}, a {0} destroy {1} houses and caused {3, number, currency} of damage.", "hurricane", 99, new GregorianCalendar(1999, 0, 1).getTime(), 10.0E8); )
- 格式化选项choice
String pattern = "On {2,date,long}, {0} destroyed {1,choice,0#no houses|1#one house|2#{1} houses}" + " and caused {3,number,currency} of demage." // no houses |1| one houss |2| {1} houses
7.7 文本文件和字符集
- 读写文件
PrintWriter out = new PrintWriter(filename, "Windows-1252"); // 获得最佳编码机制 Charset platformEncoding = Charset.defaultCharset();
- 针对不同平台行结束符不同
out.printf("Hello%nWorld%n"); // Windows上会产生Hello\r\nWorld\r\n // 其余平台上会产生Hello\nWorld\n
- JDK工具native2ascii可以将本地字符编码转换成普通的ASCII, 也可以逆向使用