1z-808复习 第6章

Exceptions

  • 区分checked excpetion,unchecked exception,Errors
  • Create a try-catch block and determine how exceptions alter normal program flow
  • Describe the advantages of Exception handling
  • Create and invoke a method that throws an exception
  • Recognize common exception classes (such as NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException, ClassCastException)

Understanding Exceptions

The Role of Exceptions

Exception是Java在处理中遇到的问题。

Understanding Exception Types

所有Exceptions或Errors的父类是Throwable。 Error是指非常严重并且程序中无法复原的错误。 Runtime exceptions are also known as unchecked exceptions.
checked Exception是指所有不是从RuntimeException派生出来的Exception。
Java中有一条规则叫“handle or declare rule”,是指对于checked exception
要么有handle的代码,要么有在method signature 里declare的代码。

Throwing an Exception

第一种方法是写的代码本身有错,执行时

Using a try Statement

try后面不能少大括号,即使只有一条语句。
不能少catch语句。

Adding a finally Block

finally里面的语句,无论是否发生Exception,都会被执行。 finally一般用在最后关闭文件句柄,关闭数据库连接。

Catching Various Types of Exceptions

可以写多个catch块,但是如果一个都执行不到,会出compile error,
也就是说在try块中一定要保证会出catch的Exception。

只有先子类Exception,再父类Exception才有可能被执行到。 反过来就会发生上面所说的执行不到的CompileError。

Throwing a Second Exception

Recognizing Common Exception Types

Runtime Exceptions

Checked Exceptions

Errors

Calling Methods That Throw Exceptions

Subclasses

在子类中方法override时,不可以增加父类中没有declare的Exception。
设想一下如果增加了,会有什么问题。 可以写调用父类的方法,并且不处理任何Exception。 通过多态性来考虑,如果子类也有同样的方法,也可以传入子类。 这时候应该处理还是不处理错误就变得不明了。

Printing an Exception

public static void main(String[] args) {
    try {
        hop();
    } catch (Exception e) {
        System.out.println(e);
        System.out.println(e.getMessage());
        e.printStackTrace();
    }
}
private static void hop() {
    throw new RuntimeException("cannot hop");
}

上面是打印Exception的三种方法。

Summary

Exam Essentials

Review Questions

1z-808复习 第4章

Methods and Encapsulation

Designing Method

例如:public final void nap (int minutes) throws InterruptedException { }

Access Modifiers

  • public 任何地方都可以调用该方法。
  • private 只有方法所在的类内部可以调用该方法。
  • protected 只可以从同一package的类,或者子类中调用该方法。

Default(package private) Access:只能从同一package调用
所以说Default Access和protect还是有一些微小的区别。

package Exam;
public class ParentClazz {
    protected void methodA() {}
    void methodB() {}
}

package Test;
import Exam.ParentClazz;
public class ChildClazz extends ParentClazz {
    void methodC(){
        /*methodA可以被同一package或者子类调用,
        *因此虽然不在同一package,这里仍然可以调用。
        *default access只能在同一package下调用
        *这里是子类,不在同一package下,因此methodB是无法调用的。*/
        super.methodA();
        super.methodB();//メソッド methodB() は型 ParentClazz で不可視です
    }
}

Optional Specifiers

可以有0个或多个,没有顺序,并不是所有都能同时出现。

  • static:静态方法
  • abstract:抽象方法,后面不能有方法体。
  • final:方法不能被重载。
  • synchronized:同期
  • native:用于同其他语言的代码交互
  • strictfp:方便浮点计算 final public void walk7() {} 可以编译运行,java虽然支持但不推荐。

Return Type

如果是void,可以省略return或者用 return;

Method Name

只要符合变量名的规则就可以了。

Parameter List

可以没有变量,但不能省略括号。

Optional Exception List

多个的时候,用逗号分开。
定义的时候用throws,使用的时候用throw(没有s)

Method Body

除了抽象方法和接口,不可以省略。

Working with Varargs

只可以有一个可变参数
多个参数时,可变参数必须在最后一个位置上。
虽然是一个数组,但和数组有一些细小的区别。

调用带有可变参数的函数时注意以下几点

  1. 可以传入一个array
  2. 或者传入array的元素就可以了,java会自动创建array
  3. 可以省略可变参数,java自动创建一个length为0的array
  4. 可以传入null,但会空指针异常
public static void walk(int start, int... nums) {
    System.out.println(nums.length);
}
walk(1, null);

虽然可以编译,但运行时会出NullPointerException
因为会把会去判断null数组的length

Applying Access Modifiers

Private Access

private: Only accessible within the same class

default (package private) access

default (package private) access: private and other classes in the same package

Protected Access

protected: default access and child classes

public Access

public: protected and classes in the other packages

Designing Static Methods and Fields

  • 每个类都有一套自己的实例变量的拷贝。
  • 所有的类共享一套实例方法代码的拷贝。
  • 从每个类的实例都可以调用这套实例方法,想多少次就多少次。
  • 每次调用实例方法时,都会在stack上开辟空间存放“方法参数”以及“local变量”。

  • 静态方法和实例方法相同,也只有一套代码拷贝,
    也同样在stack上存放“方法参数”以及“local变量”。

静态方法的两个目的:

  • 为哪些不需要对象状态的工具类提供便利。
    既然不需要访问实例变量,那么自然就不需要生成类对象再调用方法了。
    直接使用类的静态方法使得操作非常便利。
  • 为需要在所有实例中共享的实例提供操作,例如计数器。

Calling a Static Variable or Method

//最普通的写法,结尾打印0
System.out.println(Koala.count);
//通过类的实例来访问其静态变量或静态方法
//没有问题,这也打印了0;
Koala k = new Koala();
System.out.println(k.count); // k is a Koala
//结果是0,null的实例怎么会打出0?
//原因是访问的是静态变量,并不属于某个实例,因此即使实例为null,
//静态变量仍然为0
k = null;
System.out.println(k.count); // k is still a Koala

Static vs. Instance

静态方法不能直接访问instance variable,但可以在内部通过new一个局部变量实例,然后访问该局部变量的方法来绕过。

Static Variables

private static final ArrayList<String> values = new ArrayList<>();
public static void main(String[] args) {
    values.add("changed");
}

static final是说这个values参照的对象不能变,但对象的内容可以变。

Static Initialization

private static int one;
private static final int two;
private static final int three = 3;
private static final int four; // DOES NOT COMPILE
//上面这行编译没通过是因为在哪里都没有初始化这个变量
//下面这个code块就是专门对static变量的初始化块
static {
    one = 1;
    two = 2;
    //不允许第二次赋值
    three = 3; // DOES NOT COMPILE
    //不允许第二次赋值
    two = 4; // DOES NOT COMPILE
}

Static Imports

import static java.util.Arrays.asList;
asList("one"); //OK 可以直接用asList
Arrays.asList("one");//NG ,如果用Arrays,因为没有import所以会出编译错误

Passing Data Among Methods

Java是传值的语言。
这意味着对于变量,会产生一个拷贝,然后将其传给方法。任何在方法中的赋值都不会影响caller。

对于参照类型,同样产生了一个指向对象的参照变量拷贝,并且把该拷贝传给方法。 由于在方法中,对参照对象的修改使得该对象本身被修改了,看上去跟参照传递很类似。

但和参照传递的差别如下

public static void main(String[] args) {
    StringBuilder b = new StringBuilder().append("bbbb");
    aaaaa(b);
    System.out.println("b.toString():"+b.toString());
    ccccc(b);
    System.out.println("b.toString():"+b.toString());
}
public static void aaaaa(StringBuilder a) {
    a = new StringBuilder().append("aaaa");
    System.out.println("a.toString():"+a.toString());
}
public static void ccccc(StringBuilder c) {
    c = c.append("cccc");
    System.out.println("c.toString():"+c.toString());
}

打印结果:
a.toString():aaaa
b.toString():bbbb
c.toString():bbbbcccc
b.toString():bbbbcccc

可见,如果不是修改参照对象本身的内容,而是把参照的对象都修改了之后,再修改内容,
结果就不会外面的变量有影响了。

Overloading Methods

函数名相同,但不同的函数签名,就可以函数重载Method overloading

Overloading and Varargs

public void fly(int[] lengths) { }
public void fly(int... lengths) { } // DOES NOT COMPILE

fly(new int[] { 1, 2, 3 });
fly(1, 2, 3);

Obviously, this means they don't compile exactly the same.
The parameter list is the same, though,
and that is what you need to know with respect to overloading for the exam.

Autoboxing

public void fly(int numMiles) { }
public void fly(Integer numMiles) { }

函数重载本身没问题,正常编译了两个方法。
调用的时候,按照更详细原则,如果是通过int调用,则调用。

Putting It All Together

Default Constructor

把构造函数定义成private很适合只提供static方法的Util工具类。

Overloading Constructors

通过this的构建语句,必须在该方法体内的第一行

Final Fields

在构建方法内,可以对final实例变量进行复制。
在构建方法执行完之前,必须都初始化完所有的变量。

Order of Initialization

  1. 如果有superclass,则先初始化父类
  2. 静态变量和初始化,根据它们在程序中的出现顺序。
  3. 实例变量和初始化,根据它们在程序中的出现顺序。
  4. 构建函数

The last static block, on line 7, calls new to instantiate the object.
主要是这句话想不通,为什么在调用构造函数时,突然就转向调用实例变量

Encapsulating Data

Creating Immutable Classes

创建一个不带Setter方法的的类,那么这个JavaBean类的成员在初始化后就无法改变了。
但这只是primitive类型没问题,如果是参照类型,还是存在问题。
必须用下面的方法,防止参照泄露给外部。

public Mutable(StringBuilder b) {
    builder = new StringBuilder(b);
}
public StringBuilder getBuilder() {
    return new StringBuilder(builder);
}

初始化时通过参照生成了一个内部用的,并且getter方法交给外部时也包了一层。

测试

1.BC //final加在方法前意味着该方法不能被继承,而private 方法隐含着不能被继承
2.AD〇
3.ACDG      // G是错的, int怎么可以赋值null呢,最多是0;
4.ABG〇
5.DFG       // F是错的 howMany(true, {true, true});虽然不可以
               但可以 boolean[] bol ={true,true,false};howMany(true, bol);
               大括号的用法不能直接用,需要理解一下。
6.BD        // B是错的,英文问题,讲的是class不可视,不是说private和package private的变量
7.BCDF〇
8.BCE〇
9.CE〇
10.B〇
11.BE〇
12.D〇
13.D        // 对bench的理解有误,这里会出两个错误,定义的地方会出没有初期化的错误。
               而在15行会出一个再次赋值的错误。
14.C        // B是对的 import static java.util.Collections.*; 
               或者import static java.util.Collections.sort; 
               C的方法参数是废话,不用引用。
15.E〇 不能理解,primitive怎么会autoboxing
16.B〇
17.BDE〇
18.C        // G是对的,main方法在同一个类之内,所以private的constructor也是能访问到的。
            // F的理解是错误的,如果有缺省构造函数,
               说明一个用户定义的构造函数也没有,在构造函数以外部分是不能用this()的。
               只有自己写一个空构造函数,再写一个带参数的构造函数,才可以在后者里面调用this()。
19.ABC      // BC是错误的,因为constructor必须跟类名一致,不一致就不是constructor,也就必须要有返回值
            // G是对的,因为有返回值,所以只是个普通方法,没有constructor。
20.E〇
21.C〇
22.E〇(u u  ucrcr)
23.A〇
24.CE       // B也是对的,在instance method里面更改static当然是没有问题的,反之才不可以。
25.AE〇
26.A〇
27.C〇
28.AD       //F也是对的,可以省略String让lambda自己推断类型,也可以指定
29.AF〇

1z-808复习 第3章

Core Java APIs

Creating and Manipulating Strings

理解什么是 concatenation,immutability, string pool, common methods, method chaining

Concatenation

Immutability

The trade-off for the optimal packing is zero flexibility.
打包的最优取舍就是零灵活性

String s1 = "1";
String s2 = s1.concat("2");
s2.concat("3");
System.out.println(s2);

结果应该是"12"而不是"123",一定要注意,因为s2本身没有变成"123"
新生成的字符串因为没有任何指向它的变量,理论上应该会被回收。

The String Pool

String Pool又叫做intern pool,是JVM用来存放String的地方。
String name = "Fluffy"
是正常使用StringPool,有可能会共用。
String name = new String("Fluffy");
要求JVM不要使用StringPool,即使效率更低,也一定要新生成一个Object。

Important String Methods

length(),charAt(),indexOf()
substring(),toLowerCase(),toUpperCase()
equals() and equalsIgnoreCase()
startsWith() and endsWith()
contains() and trim()

int indexOf(char ch, index fromIndex)
int indexOf(String str, index fromIndex)
注意这个fromIndex是按照下标来的,从0到length-1

substring()

int substring(int beginIndex, int endIndex)  
String string = "animals";  
System.out.println(string.substring(3, 7)); // mals  

第二个参数是指结束的下标,
所以取第三个字符就是3,4,而取到最后就会是3,7(最大下标+1或length)

这个跟charAt有点混淆,同样是超出下标出错,
substring是8,而charAt是7

contains() str.indexOf(otherString) != -1.
同样效果的写法,不需要再写了。

replace有两种写法,一个是char,一个是CharSequence
String replace(char oldChar, char newChar)
String replace(CharSequence oldChar, CharSequence newChar)

System.out.println("abcabc".replace('a', 'A')); // AbcAbc
System.out.println("abcabc".replace("a", "A")); // AbcAbc
同样的效果。

A CharSequence is a general way of representing several classes, including String and StringBuilder.
CharSequence是表示几个类的通用方法,包括String类和StringBuilder类。

trim()会去掉字符串开头和结尾的whitespace。
whitespace的定义是,space(单字节空格),制表符(\t),换行符(\n)
另外\r (carriage return):回车
\n换行:换到下一行
\r回车:行不变,定位到左边界
Windows里面换新行用的是"\r\n"
Unix里面换新行用的是"\n"

Method Chaining

String result = "AniMaL ".trim().toLowerCase().replace('a', 'A');
虽然连着写更加明了简洁,但总共还是创建了4个String对象。
跟分开写,性能没有任何影响。

Using the StringBuilder Class

//无效率的产生了26个可以垃圾回收的字符串
String alpha = "";
for(char current = 'a'; current <= 'z'; current++)
    alpha += current;
System.out.println(alpha);

//只有一个StringBuilder对象,效率大大提高
StringBuilder alpha = new StringBuilder();
for(char current = 'a'; current <= 'z'; current++)
    alpha.append(current);
System.out.println(alpha);

Mutability and Chaining

When we chained String method calls, the result was a new String with the answer.
Instead, the StringBuilder changes its own state and returns a reference to itself!

StringBuilder sb = new StringBuilder("start");
sb.append("+middle"); // sb = "start+middle"
StringBuilder same = sb.append("+end"); // "start+middle+end"

sb和same最后内容完全相同,都是"start+middle+end"
※※※
StringBuilder的函数作用是改变StringBuilder本身的状态,然后将自己参照返回。
String的函数作用是生成一个新的String,然后将新String的参照返回
※※※

Creating a StringBuilder

StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder("animal");
StringBuilder sb3 = new StringBuilder(10);

第三行代码不是创建一个内容是"10"的StringBuilder,
而是创建一个可以容纳10个字符的StringBuilder。

StringBuilder类的两个参数,一个是size,表示其中目前含有的字符数。
另一个是capacity,表示当前这个StringBuilder能够容纳的字符数。

对于StringBuilder,Java知道它的Size会变,因此在构建StringBuilder对象时,
它会设置缺省的capacity,(缺省是16,最大容纳16个字符)

Java在字符数超过StringBuilder的capacity的时候,会自动扩展其capacity。
但扩展多大,这个今后要看。好像是16

Important StringBuilder Methods

charAt(), indexOf(), length(), and substring()

StringBuilder sb = new StringBuilder("animals");
String sub = sb.substring(sb.indexOf("a"), sb.indexOf("al"));
int len = sb.length();
char ch = sb.charAt(6);
System.out.println(sub + " " + len + " " + ch);

答案是 anim 7 s。一开始以为substring是直接操作StringBuilder对象本身,
这个认识是错误的。
特别要注意的是substring(),这个是指从StringBuilder对象中切出一个新的String对象。
因此,StringBuilder类本身并没有变化。
可以把substring()想象成一个查询,查询从fromIndex到endIndex的内容,作为一个结果返回给新的String对象。

上面的函数执行完后StringBuilder对象的内容不会有变化

下面的函数执行完后StringBuilder对象的内容会被执行结果更新

append(),insert(),delete() and deleteCharAt()
StringBuilder insert(int offset, String str)
在offset之前,插入str

StringBuilder delete(int start, int end)
StringBuilder deleteCharAt(int index)

删除以start开始,到end之前的字符串

reverse()和toString()
就是将字符串的顺序反过来
"animals" ⇒ "slamina"

StringBuilder vs. StringBuffer

StringBuffer跟StringBuilder是一样的效果,
但是效率上会慢一点,因为它是线程安全的。
String也是线程安全的。

Understanding Equality

StringBuilder one = new StringBuilder();
StringBuilder two = new StringBuilder();
StringBuilder three = one.append("a");
System.out.println(one == two); // false
System.out.println(one == three); // true

因为append之后会把内容更新并返回自身,因此three和one相同。

String x = "Hello World";
String y = "Hello World";
System.out.println(x == y); // true


String x = "Hello World";
String z = " Hello World".trim();
System.out.println(x == z); // false


String x = new String("Hello World");
String y = "Hello World";
System.out.println(x == y); // false

因此千万不要用==来比较两个字符串

String类有equals方法,因此比较的是String内容是否一致。
而StringBuilder类没有equals方法,因此比较的是是否参照了同一个对象。

Understanding Java Arrays

Creating an Array of Primitives

int[] numbers = new int[3]; // 创建一个全是0的大小是3的整数型数组
int[] numbers2 = new int[] {42, 55, 99}; //创建一个大小是3的数组并初始化
int[] numbers2 = {42, 55, 99};//简略后

注意:使用大括号来赋值

int[] numAnimals;
int [] numAnimals2;
int numAnimals3[];
int numAnimals4 [];

上面4个声明变量的方法,结果完全相同。

int[] ids, types;
int ids[], types;

区别上面两行代码的结果,一个是生成了两个数组;
另一个是生成了1个数组和1个整形变量。

Creating an Array with Reference Variables

public class ArrayType {
public static void main(String args[]) {
    String [] bugs = { "cricket", "beetle", "ladybug" };
    String [] alias = bugs;
    System.out.println(bugs.equals(alias)); // true
    System.out.println(bugs.toString()); // [Ljava.lang.String;@160bc7c0
    System.out.println(Arrays.toString(bugs));//[cricket, beetle, ladybug]
} }

因为两个变量都参照了同一个对象,当然就是相等的。
L means it is an array, java.lang.String is the reference type, and 160bc7c0 is the hash code.

java.util.Arrays.toString(Object[] obj) 对每个Object.toString(),然后出力。
上面因为list的每个变量是一个String,因此就是把内容直接出力。

String[] strings = { "stringValue" };
Object[] objects = strings;
String[] againStrings = (String[]) objects;
againStrings[0] = new StringBuilder(); // DOES NOT COMPILE
objects[0] = new StringBuilder(); // careful!

strings, objects, againStrings都指向同一个object,一个String的Array。
StringBuilder对象无法放到放到String对象里去。
System.out.println(new StringBuilder().toString());
打印了一个空字符串而不是null,说明StringBuilder初始化不是null而是空字符串""。

最后一行在编译期不会发生错误,但是在执行期会出现ArrayStoreException。

Using an Array

Sorting

如果Array是数字型的则按数字大小排序
如果Array是String型的,则按字母顺序排序,“19”和“6”相比,因为1比6小,所以19排在前面
另外数字排在字母前面,大写排在小写前面

Searching

如果Arrays是排好序的,则可以使用binarySearh方法。
找到了会返回下标,找不到返回插入顺序取反之后-1的整数值。

后面学习一下二叉查询法。

Varargs

public static void main(String... args)
定义的方法在第一章里详细介绍。
使用方法在第四章里详细介绍。

Multidimensional Arrays

int[][] vars1; // 2D array
int vars2 [][]; // 2D array
int[] vars3[]; // 2D array
int[] vars4 [], space [][]; // a 2D AND a 3D array

Understanding an ArrayList

creating an ArrayList, common methods, autoboxing, conversion, and sorting.

Creating an ArrayList

Java 5 之前,用下面的方法定义。 ArrayList list2 = new ArrayList();
ArrayList list2 = new ArrayList(10);
参数10是capacity。表示生成可以放10个元素的ArrayList
java 5之后,需要指定ArrayList的元素类型。
ArrayList<String> list4 = new ArrayList<String>(); Java 7之后,可以省略右边的类型,用钻石符号"<>"来替代。
ArrayList<String> list5 = new ArrayList<>();

Using an ArrayList

Wrapper Classes

从String转回primitive和其wrapper class,这两个都可以。
int primitive = Integer.parseInt("123");
Integer wrapper = Integer.valueOf("123");

Autoboxing

List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.remove(1);
System.out.println(numbers);

结果打印的是1,而不是2的原因是,add的时候,1,2是内容。
remove的时候,1是index,如果想删除内容1,要写成
numbers.remove(new Integer(1));

Converting Between array and List

List<String> list = new ArrayList<>();
list.add("hawk");
list.add("robin");
Object[] objectArray = list.toArray();
System.out.println(objectArray.length); // 2
String[] stringArray = list.toArray(new String[0]);
System.out.println(stringArray.length); // 2

可以用list.toArray()来转换,但是转换结果是Object
如果想转成String
类型,要用list.toArray(new String[0])

反过来,从array转成List之后,这个list叫做
array backed List,并且array和List是共享一个参照。

String[] array = { "hawk", "robin" }; // [hawk, robin]
List<String> list = Arrays.asList(array); // returns fixed size list
System.out.println(list.size()); // 2
list.set(1, "test"); // [hawk, test]
array[0] = "new"; // [new, test]
for (String b : array) System.out.print(b + " "); // new test
list.remove(1); // throws UnsupportedOperation Exception

Arrays.asList(array)来转换。
这个ArrayList因为是array backed,因此是fixed siz

应为支持可变参数,因此用下面的方法更加方便的创建List
List<String> list = Arrays.asList("one", "two");

Sorting

用下面的方法排序。
Collections.sort(numbers);

Working with Dates and Times

最新的Date和Time操作类,需要做以下导入

import java.time.*; // import time classes

在后面章节,要学习create,manipulaing,formatting日期和时间

Creating Dates and Times

三个类:LocalDate,LocalTime,LocalDateTime
这3个类不带时区概念 如果需要带时区的日期,则用ZonedDateTime 这个怎么理解:
“the stroke of midnight on New Year’s.” Midnight on January 2 isn’t nearly as special, and clearly an hour after midnight isn’t as special either.
打印出来的格式类似:2015-01-20T12:45:18.401
日期加上时间,中间用字母“T”连接
创建当前日期可以用.now()静态方法。
如果要创建指定日期可以用静态方法.of()
该方法有很多overload的版本,例如:
public static LocalDateTime of(LocalDate date, LocalTime)
注意:这3个类都没有构建方法,必须用上面的静态方法来创建对象。

Manipulating Dates and Times

System.out.println(date); // 2014-01-29
date = date.plusMonths(1);
System.out.println(date); // 2014-02-28

因为不是闰年,所有2月没有29日,会自动设值28日。

System.out.println(dateTime); // 2020-01-18T19:15
dateTime = dateTime.minusSeconds(30);
System.out.println(dateTime); // 2020-01-18T19:14:30

没有操作秒的时候,秒不会显示,操作了之后,秒会自动显示。

Date和Time类跟String类似,是immutable的

LocalDate date = LocalDate.of(2020, Month.JANUARY, 20);
date.plusDays(10);
System.out.println(date);

如果不把计算结果返回,最终打印出来的还是1月20日

Working with Periods

LocalDate.of(1970, Month.JANUARY, 1).toEpochDay() 最后的值为0 Period没法用链式表达 Period wrong = Period.ofYears(1).ofWeeks(1); 最终的期间结果是1个礼拜而不是1年零7天。 日期的操作函数plusDate但效果不同,容易混淆,要注意。

Formatting Dates and Times

DateTimeFormatter类来控制输入的日期格式 两种格式化的方法,一种是直接用类自带的.format方法。

LocalDate date = LocalDate.of(2020, Month.JANUARY, 20);
LocalTime time = LocalTime.of(11, 12, 34);
LocalDateTime dateTime = LocalDateTime.of(date, time);
System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println(time.format(DateTimeFormatter.ISO_LOCAL_TIME));
System.out.println(dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

第二种是用DateTimeFormatter类的format去格式化日期时间类。

DateTimeFormatter shortDateTime = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
System.out.println(shortDateTime.format(dateTime)); // 1/20/20
System.out.println(shortDateTime.format(date)); // 1/20/20
System.out.println(shortDateTime.format(time)); // UnsupportedTemporalTypeException

DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);生成想要的格式的DateTimeFormatter。

注意有ofLocalizedDate,ofLocalizedDateTime,ofLocalizedTime三种类型,用相应的方法。

预定义格式有两种,SHORT和MEDIUM
如果需要自定义格式,需要用下面的格式。
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");

Parsing Dates and Times

Summary

Exam Essentials

1.DA
2.ACD
3.BCE
4.B
5.D
6.B
7.E
8.AED
9.C
10.F
11.F
12.A
13.F
14.AC
15.BCE
16.D
17.F
18.AC
19.C
20.D
21.C
22.D
23.A
24.B
25.E
26.C
27.B
28.DF
29.D
30.E
31.F
32.

第二章 XML

2.1 XML 概述

属性文件是一种单一的平面层次结构,并且要求键值是唯一的。
XML能够表示层次,解决了上述问题。
XML与HTML都是标准通用标记语言(SGML)的衍生

XML和HTML的重要区别:
  • XML是大小写敏感的,例如<H1><h1>是不同的XML标签
  • 在HTML中,如果从上下文可以分清哪里是段落或列表项的结尾,
    那么结束标签就可以省略,而在XML中结束标签绝对不能省略。
  • XML中,只有单个标签而没有相对应的结束标签的元素必须以/结尾,
    比如<img src="coffeecup.png"/> 这样,解析器就知道不需要查找</img>标签
  • XML中,属性值必须用引号括起来。
  • 在HTML中,属性名可以没有值。
文档的结构

XML文档应当以一个文档头开始

<?xml version="1.0" encoding="UTF-8"?>

因为建立SGML是为了处理真正的文档,因此XML文件叫做文档。
尽管许多XML文件是用来描述通常不称作文档的数据集的。

●文档头之后通常是文档类型定义(Document Type Definition,DTD
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
文档类型定义的是确保文档正确的一个重要机制,但同文档头一样也不是必需的。

XML文档正文包含根元素,根元素包含其他元素
元素可以有子元素或者文本。设计时应该避免混合式内容

<font>
    Helvetica
    <size>36</size>
</font>

XML文档包含属性,属性只应该用来修改值得解释,而不是用来指定值。
在HTML中属性的使用规则很简单:凡是不显示在网页上的都是属性。

一些标记:

  • 字符引用(&#十进制值,&#x16进制值)
    &#233,&#xE9
  • 实体引用(&name)
    &lt;&gt;&amp;&quot;&apos;
  • CDATA部分,用<![CDATA[]]>来界定其界限,是字符数据的一种特殊形式。
    可以用来显示包括<,>,&之类的字符的字符串
    <![CDATA[<&> are my favorite delimiters]]>
    不能包含字符串]]
  • 处理指令(processing instruction)用<?和?>来限定其界限。
    <?xml-stylesheet href="mystyle.css" type="text/css"?>
  • 注释(comment)用<!--和-->来限定其界限。
    不能包含字符串 --,也绝不应该包含隐藏的命令。

2.2 解析XML文档

用解析器解析XML文档。

  1. 读入一个文件
  2. 确认这个文件具有正确的格式
  3. 将其分携程各种元素。
  4. 提供访问这些程序的接口

两种解析器:

  • 树形解析器,将读入的XML文档转换成树结构。
    例如:文档对象模型(Document Object Model, DOM)解析器,
    实现简单,但生成的树结构将会消耗大量内存。
  • 流机制解析器,在读入XML文档时生成相应的事件。
    XML简单API(Simple API for XML, SAX)解析器
    不关心上下文←这句话怎额理解

DOM解析器的接口已经标准化了,Apache Organization和IBM都编写了这些接口的解析器。
JavaXML处理APIJava API for XML Processing,JAXP)库
使得实际上可以以插件形式使用这些解析器中的任意一个。

另外JDK中也包含了一个自己的DOM解析器

注意:如果使用输入流作为输入源,那么对于那些以本文档的位置作为相对路径而被引用的文档,
解析器将无法定位,比如在同一目录中的DTD。但是可以通过安装一个“实体解析器”来解决问题。

<font>
    <name>Helvetica</name>
    <size>36</size>
</font>
NodeList children = root.getChildNodes();
for (int i=0;i<children.getLength();i++) {
    Node Child = children.iten(i);
}

预期取回来两个子元素,但解析器报告却说有5个,另外3个分别为,
<font>和<name></name>和<size></size>和</font>之间的空白。

如果只希望得到子元素,则可以忽略空白。

NodeList children = root.getChildNodes();
for (int i=0;i<children.getLength();i++) {
    Node Child = children.iten(i);
    if (child instanceof Element){
        Element childElement = (Element) child;
        ...
    }
}

如果文档有DTD就可以做的更好。解析器知道哪些元素没有文本节点的子元素,
而且会帮你剔除空白字符。

NodeList children = root.getChildNodes();
for (int i=0;i<children.getLength();i++) {
    Node Child = children.iten(i);
    if (child instanceof Element){
        Element childElement = (Element) child;
        Text textNode = (Text)childElement.getFirstChild();
        String text = textNode.getData().trim();
        if (childElement.getTagName().equals("name")){
            name = text;
        }
        if (childElement.getTagName().equals("size")){
            name = Integer.parseInt(text);
        }
    }
}

对getData返回值调用trim是个好主意,因为标签在不同行的时候,换行符和空格都在文本里。
对第二层元素,如果要遍历,同样还得用NodeList来循环,
上例因为知道下面已经没有子元素了。所以直接用FirstChild方法取得内容。

如果要枚举节点的属性,可以调用getAttributes方法,它返回一个NameNodeMap对象。
同样遍历NamedNodeMap来遍历子节点,通过getNodeName和getNodeValue来获取属性名和值

NameNodeMap attributes = element.getAttributes();
for(int i=0; i< attributes.getLength(); i++){
    Node attribute = attributes.item();
    String name = attribute.getNodeName();
    String value = attribute.getNodeValue();
}
//如果知道属性名,想取属性值
element.getAttribute("unit");

2.3 验证XML文档

上面是通过程序,验证文档的结构,以及进行错误的检查,程序非常冗长。
XML解析器可以自动校验某个文档是否具有正确的结构。

指定文档结构,提供一个文档类型定义(DTD)或一个XML Schema定义。 其包含了用于解释文档应如何构成的规则,这些规则指定了每个元素的合法子元素和属性。

例:DTD含有一个规则,font元素必须总是有两个子元素,name和size <!ELEMENT font(name,size)>

同样的XML Schema约束表示如下:

<xsd:element name = "font">
    <xsd:sequence>
        <xsd:element name = "name" type = "xsd:string"/>
        <xsd:element name = "font" type = "xsd:int"/>
    </xsd:sequence>
</xsd:element>

DTD相比,XML Schema可以表达更加复杂的验证条件。
DTD语法不同,Schema使用XML,这为处理Schema文件带来了方便。

XML Schema虽然被设计替代DTD,但比较复杂,所以现在还没有得到普遍采纳。

2.3.1 文档定义类型

提供DTD的方式有多种。可以像下面这样将其纳入XML文档中。

<?xml version="1.0"?>
<!DOCTYPE configuration [
    <!ELEMENT configuration ... >
    more rules
    ...
]>
<configuration>
...
</configuration>

规则被纳入到了DOCTYPE声明中,代码块使用了[]来限定其界限。
文档类型必须匹配根元素的名字

DTD存储在外面,然后在XML文档中引用比较常见。

<!DOCTYPE configuration SYSTEM "config.dtd">
<!DOCTYPE configuration SYSTEM "http://myserver.com/config.dtd">

这里的SYSTEM 有什么作用?

如果使用的DTD是相对Url,name要给解析器一个File或者URL对象,而不是InputStream。
如果必须从一个输入流来解释,需要一个实体解析器

来源于SGML的,用于识别“众所周知”DTD的机制

<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems,Inc.//DTD Web Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

如果XML处理器知道如何定位带有公共标识符的DTD,那么就不需要URL了。

指定描述合法的元素属性的规则。通用语法:
<!ATTLIST element attribute type default> 例如: <!ATTLIST font style (plain|bold|italic|bold-italic) "plain">
<!ATTLIST size unit CDATA #IMPLIED>

2.3.2 XML Schema

如果要在文档中引用Schema文件,需要在根元素中添加属性,
例如

<?xml version ="1.0"?>
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsl:noNamespaceSchemaLocation="config.xsd">
...
</configuration>

这个声明说明Schema文件config.xsd会被用来验证文档。
如果使用命名空间,语法就更加复杂了。详细请参见XML Schema指南
前缀xsi是一个命名空间别名(namespace alias)

2.3.3 实用实例

2.4 使用XPath来定位信息

2.5 使用命名空间

2.6 使用流机制解析器

2.6.1 使用SAX解析器
2.6.2 使用StAX解析

2.7 生成XML文档

2.7.1 不带命名空间的文档
2.7.2 带命名空间的文档
2.7.3 写出文档
2.7.4 示例生成SVG文件
2.7.5 使用StAxXML文档

2.8 XSL转换

Java学习笔记 - 流

流与文件

Java中用于输入和输出的 应用编程接口(Application Programming Interface, API)。

  • 如何访问文件与目录,
  • 如何以二进制格式和文本格式来读写数据。
  • 对象序列化机制
  • 新I/O(nio)
  • 正则表达式

1.1 流

  • 可以从其中读入一个字节序列的对象称做输入流
  • 可以向其中写入一个字节序列的对象称做输出流

字节的来源可以是文件,也可以是网络连接,甚至是内存块。

抽象类InputStream和OutputStream构成了输入输出I/O类层次构成的基础

面向字节的流不便于处理以Unicode形式存储的信息
所以从抽象类Reader和Writer中继承出来了一个
专门用于处理Unicode字符的单独的类层次结构。
这些类拥有的读入和写入操作都是基于两字节的Unicode码元的, 而不是基于单字节的字符。

1.1.1读写字节

InputStream类有一个抽象方法。 abstract int read() 这个方法读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1。
在设计具体的输入流时,必须覆盖这个方法以提供适用的功能,
InputStream类还有若干个非抽象的方法。它们可以读入一个字节数组,
或者跳过大量的字节。这些方法都要调用抽象的read方法, 因此各个子类都只需要覆盖这个方法。

与此类似,OutputStream类定义了下面的抽象方法。
abstract void write(int b)

read和write方法在执行时都将阻塞,直至字节确实被读入或者写入。
available方法使我们可以去检查当前可读入的字节数量,这意味着下面这样的代码不可能被阻塞。

int byteAvailable = in.available();
if (byteAvailable > 0){
    byte[] data = new byte[byteAvailable];
    in.read(data);
}

当完成对流的读写时,应该通过调用close方法来关闭它。
即使某个流提供了使用原生的read和write功能的某些具体方法,
应用系统的程序员还是很少使用它们,因为大家最终要处理的数据是数字,字符串和对象,
而不是原生字节。

Java提供了众多从基本的InputStream和OutStream类导出的类, 这些类使我们可以处理那些以常用格式表示的数据,而不只是字节。

完整的流家族 按照使用方法可以划分成两类。
一类是处理字节和处理字符的两个单独层次。
InputStream和OutStream累可以读写单个字节或字节数组。
要想读写字符串和数字等,就需要功能更加强大的子类。
例如:DataInputStream,和DataOutputStream
可以以2进制格式读写所有的基本Java类型。
例如:ZipInputStream,和ZipOutputStream 可以以常用的ZIP压缩格式读写文件

另一方面,对于Unicode文本,可以使用抽象类Reader和Writer的子类。
Reader和Writer的基本方法与InputStream和OutputStream中的方法类似。

4个接口:Closeable,Flushable,Readable,Appendable
Reader,Writer,InputStream,OutputStream都实现了Closeable接口
注意:java.io.Closeable接口扩展了java.lang.AutoCloseable接口。
因此,对任何Closeable进行操作时,都可以使用try-with-resource语句。

Closeable接口的close方法只抛出IOException,
而AutoCloseable.close方法可以抛出任何异常。

OutputSream和Writer还实现了Flushable接口

Readable接口只有一个方法。
int read(CharBuffer cb)
CharBuffer类拥有按顺序和随机地进行读写方法,它表示一个内存中的缓冲区
或者一个内存映像的文件(1.7.1)

Appendable接口有两个用于添加单个字符和字符序列的方法:
Appendable append(char c)
Appendable append(CharSequence s)

1.1.3 组合流过滤器

FileInputStream和FileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流。 只需要向构造器提供文件名或者文件的完整路径。

java.io中的类都把相对路径名解释为以用户工作目录开始,
可以通过调用System.getProperty("user.dir")来获取。

由于反斜杠在java中是转义字符、正确写法为“C:\\Windows\\win.ini

DataInputStream类没有任何从文件中或许数据的方法。
Java的机制来分离两种职责。
FileInputStream可以从文件和其他更外部的位置上获取字节,
而DataInputStream可以将字节组黄德奥更有用的数据类。

FileInputStream fin = new FileInputStream("employee.dat");
DataInputStream din = new DataInputStream(fin);
double s = din.readDouble();

可以通过嵌套过滤器来添加多重功能,例如想使用缓冲机制

DataInputStream din = new DataInputStream(
    new BufferedInputStream(
        new FileInputStream("employee.dat")))

1.2 文本输入与输出 在保存数据时,可以选择二进制格式或文本格式。
- 本章讨论文本格式的I/O,
- 然后在第1.3节“读写二进制数据”中讨论二进制格式的I/O

在存储文本字符串时,需要考虑字符编码(character encoding)方式。
UTF-16编码方式中,字符串“1234”编码为“00 31 00 32 00 33 00 34”
也就是“1”这个字符使用一个“00 31”这个4个字节的16进制数来表示。
还有很多编码方法,比如“ISO 8859-1”这种在美国和西欧最常用的编码方式中。
字符串被写出为“31 32 33 34”,其中并没有任何0字节。

OutputStreamWriter类使用选定字符编码方式,把Unicode字符流转换成字节流。
- 字符流:
- 字节流:
就像把字符串(字符流)转化成二进制流(字节流)。
而InputstreamReader类将包含字节(用某种字符编码方式表示的字符)的输入流,转换成可以产生Unicode码元的读入器。
InputStreamReader in = new InputStreamReader(System.in);
这个输入流读入器缺省使用主机系统所使用的默认字符编码方式, 例如西欧采用ISO 8859-1
可以通过在InputStreamReader的构造器中进行指定的方式来选择不同的编码方式。
InputSteamReader in = new InputStreamReader(FileInputStream("krenlin.data"),"ISO8859_5");

1.2.1 如何写出文本输出

对于文本输出,可以使用PrintWriter。这个类拥有以文本格式打印字符串和数字的方法,
它甚至还有一个将PrintWriter链接到FileWriter的便捷方法。
PrintWriter out = new PrintWriter("employee.txt"); 等同于
PrintWriter out = new PrintWriter(new FileWriter("employee.txt");
用print就能打出内容。内容被输出到写出器out,然后被转换成字节,

22.10 队列和优先队列

队列:先进先出,元素被追加到队列末尾,然后从队列头删除。
优先队列:元素被赋予优先级,最高优先级的元素首先被删除。

Queue

Queue接口用附加的插入,提取和检验操作来扩展java.util.Collection。 offer是添加一个元素。
poll和remove都是取并删除队列头的元素。只是取不到的时候一个是null,一个是异常。
peek和element都是获取队列头的元素。只是取不到的时候一个是null,一个是异常。

双端队列Deque和链表LinkedList

LinkedList类实现了Deque接口,Deque又扩展了Queue接口
Deque支持在两端插入和删除元素。deque是“双端队列(double-ended queue)”的简称
Deque接口用附加的从队列两端插入和删除元素的办法扩展Queque接口。

PriorityQueue类实现一个优先队列,默认情况下,
优先队列使用Comparable以元素的自然顺序进行排序。
拥有最小数值的元素被赋予最高优先级

Java集合框架深度学习笔记 - HashMap和Collections

22.11 图(Map)

Map是一种依照键值存储元素的容器。
在Map中,键值可以是任意类型的对象。Map中不能有重复的键值
每个键值对应一个值,一个键值和它的对应值构成一个条目,
真正在map中存储的是这个条目。

图有三种:散列图HashMap,链式散列图和树形图。

Map接口提供了查询,更新和获取集合的值和集合的键值的方法。
更新方法(update methods),包括clear, put, putAll, remove
查询方法(query method),containsKey, containsValue, isEmpty和size

containsKey(Object key):检查map中是否包含指定键值的映射
containsValue(Object value):检查map中是否包含指定值的映射

可以使用方法keySet()来获取一个包含map中键值的规则集,
也可以使用方法values()获得一个包含map中值的集合。

方法entrySet()返回一个实现Map.Entry<K,V>接口的对象集合,
这里的Entry是Map接口的一个内部接口,
该集合中的每个对象都是底层图中的一个特定的键/值对。

AbstractMap类是一个便利类,实现了Map接口中除了entrySet()方法以外的所有方法
SortedMap接口扩展了Map接口,并保持映射以键值升序的顺序排列。
它还有附加的方法firstKey()和lastKey(),以返回最低键值和最高键值。
headMap(toKey)返回值小于toKey的那部分map,
而tailMap(fromKey)返回键值大于等于fromKey那部分Map。

LinkedHashMap类用链表来实现扩展HashMap类,它支持图中条目的排序。
条目的排序键值的排序的区别在于
键值的排序是指键值本身值的排序,这个是SortedMap的特质
条目的排序是指条目插入的顺序(insertion order),
以及访问的顺序(access order)等等,跟条目本身的值没有关系。
无参数构造是以插入顺序来创建LinkedHashMap对象的。
如果要按访问顺序,则要调用另一个构造函数设置accsessOrder
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
true:access-order;false:insertion-order

TreeMap在遍历排好顺序的键值时是很高效的。
键值可以使用Comparable接口和Comparator接口来排序。

如果使用TreeMap的无参构造函数创建一个TreeMap对象。
假定元素的类实现了Comparable接口,则可以使用Comparable接口中的
方法compareTo来对图内的元素进行比较。

要使用比较器,必须使用构造方法TreeMap(Comparatorcomparator)来创造有序图。

  • 如果更新map时不需要保持图中的元素顺序,就使用HashMap
  • 如果需要保持图中元素的插入顺序或访问顺序,就用LinkedHashMap
  • 如果需要使用图按照键值排序,就使用TreeMap

22.12 单元素和不可变的集合和图

单元素就是单例singleton

Collections类:

  • 包含了线性表和集合的静态方法。
  • 包含了用于创建单元素的规则集,线性表和图的方法,以及用于创建不可变规则集。 singleton :Set singletonList :List singletonMap :Map

Collections类中定义了3个常量: EMPTY_SET,EMPTY_LIST,EMPTY_MAP singleton :仅含一个条目的不可变规则集Set singletonList :仅含一个条目的不可变线性表List singletonMap :仅含一个单一映射的不可变图Map

Collections类中定义了6个用于创建只读集合的静态方法: unmodifiableCollection(Collection c) 只读集合可保护集合中的数据以防止被修改,同时对只读操作提供了更好的性能。