1Z0-809复习 第2章 设计模式和原则 Interface

Design Patterns and Principles

  • 会申明,实现,继承,接口,并且使用override注释
  • 会写lambda表达式
  • 会用java.util.function package里的
    Predicate,Function,Consumer,Supplier
  • 实现封装
  • 实现继承,包括可是修饰符和composition
  • 创建单例类和不可修改类

Designing an Interface

有时间复习一下OCA中关于interface的

Purpose of an Interface

Introducing Functional Programming

functional interface只有唯一抽象方法的接口。

Defining a Functional Interface

虽然不是必须,显示使用@FunctionalInterface来申明函数接口比较好。
如果已经标记了@FunctionalInterface注释,但接口中没有,或有多个抽象方法,
那么编译器会识别,报出编译错误。

另一问题是,如果不显示声明@FunctionalInterface
其他开发者有可能把这个接口当成函数接口用,某一天在这个接口中增加了一个方法之后,
其他开发者的程序会全部出错。

如果接口中只有一个抽象方法,那即使是有很多个default/static方法, 因为都不是抽象方法,整体上还是只有一个抽象方法,因此还是一个函数接口。

个人总结:函数接口是只有唯一一个抽象方法的接口。

Implementing Functional Interfaces with Lambdas

This expression means that Java should call a method with an Animal parameter that returns a boolean value that’s the result of a.canHop() . We know all this because we wrote the code. But how does Java know? 表达式意味着应该调用一个方法,这个方法参数是一个Animal类。并且返回一个boolean值(a.canHop的结果)。

Java relies on context when figuring out what lambda expressions mean.
java在判断lambda表达式时,是根据上下文来判断的。 We are passing this lambda as the second parameter of the print() method.
print的第二个参数是一个lambda表示。 That method expects a CheckTrait as the second parameter.
声明的地方,第二个参数是一个CheckTrait接口 Since we are passing a lambda instead, 不传入接口,而是传入一个lambda表达式, Java treats CheckTrait as a functional interface and tries to map it to the single abstract method: 于是java把checktrait当做一个functional interface。然后把表达式map到该函数接口的唯一的抽象方法。

Applying the Predicate Interface

Implementing Polymorphism

多态允许一个对象有多个形式,一个对象可以用自己本身类的参照,父类的参照,以及其实现的接口的参照来访问。
给一个对象指定了一个参照类型,那么只有这个参照类型里面定义了成员变量和方法可以被访问。

Distinguishing between an Object and a Reference

可以把对象想象成JRE在内存中分配的实体。

  1. 对象的类型决定了在内存中有哪些属性(这些属性是属于该对象的)。
  2. 参照的类型决定上述的属性(成员变量和方法)中哪些可以被访问。

根据参照类型,有些成员不能被访问,但可以通过显式的强制转换后来访问。

Casting Object References

Primate primate = lemur;
Lemur lemur2 = primate; // DOES NOT COMPILE
Lemur lemur3 = (Lemur)primate;
System.out.println(lemur3.age);
  1. 从子类转换到父类不需要显式的强制转换。
  2. 从父类转换到子类需要显式的强制转换。
  3. 编译器不许没有相关的类之间的互相转换。
  4. 就算是编译器没报错,如果对象不是该类型,那么还是会出runtime错。
public class Rodent {
}
public class Capybara extends Rodent {
    public static void main(String[] args) {
        Rodent rodent = new Rodent();
        //如果这样就不会出错,Rodent rodent = new Capybara();
        Capybara capybara = (Capybara)rodent; // Throws ClassCastException at runtime
    }
}

这个例子就不会出编译错,但是会出runtime错。
强制转成子类,也要从一开始这个对象就是子类才可以。

Understanding Design Principles

Encapsulating Data

Creating JavaBeans

boolean型的可以用is或者get,其他的类型只能是get/set

Applying the Is‐a Relationship

The is‐a relationship is also known as the inheritance test. The fundamental result of the is‐a principle is that if A is‐a B, then any instance of A can be treated like an instance of B.

Applying the Has‐a Relationship

Composing Objects

Working with Design Patterns

Applying the Singleton Pattern

public class HayStorage {
  private int quantity = 0;
  private HayStorage() {}
  private static final HayStorage instance = new HayStorage();
  public static HayStorage getInstance() {
    return instance;
  }
  public synchronized void addHay(int amount) {
    quantity += amount;
  }
  public synchronized boolean removeHay (int amount) {
    if(quantity < amount) return false;
    quantity -= amount;
    return true;
  }
  public synchronized int getHayQuantity() {
    return quantity;
  }
}
  1. 单例是私有的,静态的。
  2. 一般通过一个共有的getInstance方法来获取。
  3. 所有构造方法都是私有的。

因为所有的构造方法都是private的,所以Singleton类是final的。

Creating Immutable Objects

就是没有setter的JavaBean。

Using the Builder Pattern

Creating Objects with the Factory Pattern

Summary

1Z0-809复习 第2章 设计模式和原则 大纲

Design Patterns and Principles

Designing an Interface

Purpose of an Interface

Introducing Functional Programming

Defining a Functional Interface

Implementing Functional Interfaces with Lambdas

Applying the Predicate Interface

Implementing Polymorphism

Distinguishing between an Object and a Reference

Casting Object References

Understanding Design Principles

Encapsulating Data

Creating JavaBeans

Applying the Is‐a Relationship

Applying the Has‐a Relationship

Composing Objects

Working with Design Patterns

Applying the Singleton Pattern

Creating Immutable Objects

Using the Builder Pattern

Creating Objects with the Factory Pattern

Summary

1Z0-809复习 第1章 Creating Nested Classes

Advanced Class Design

Reviewing OCA Concepts

Access Modifiers

Overloading and Overriding

Abstract Classes

Static and Final

Imports

Using instanceof

Understanding Virtual Method Invocation

Annotating Overridden Methods

Coding equals, hashCode, and toString

java中所有类都是直接或间接继承了Object,因此也继承了Object类的所有方法。

toString

java中的print方法,会自动调用打印对象的toString()方法。

对象如果实装了toString()方法,使得打印的对象内容更清楚明了。
譬如ArrayList,[1,2,3]
如果对象使用Object的toString()方法,例如array对象
[Ljava.lang.String;@65cc892e]

一般的做法是打印出对象的所有成员变量,但一个一个写的话非常麻烦。
Apache社区提供了上述opensource,利用其提供的方法,
可以非常方便的打印出对象中所有成员变量。

//定义build.gradle
compile("org.apache.commons:commons-lang3:3.8")
//方法重载
public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

这其中利用到了反射,在实行期取得类的信息。
所有source:

import org.apache.commons.lang3.builder.ToStringBuilder;

public class Hippo {
    private String name;
    private double weight;
    public Hippo(String name, double weight) {
        this.name = name;
        this.weight = weight;
    }
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
    public static void main(String[] args) {
        Hippo h1 = new Hippo("Harry", 3100);
        System.out.println(h1); // Harry
    }
}

执行结果:com.apibot.Hippo@78308db1[name=Harry,weight=3100.0]

//简略版
public String toString() {
    return ToStringBuilder.reflectionToString(this,ToStringStyle.SHORT_PREFIX_STYLE);
}

执行结果:Hippo[name=Harry,weight=3100.0]

equals

如果类重载了equals方法,那么利用重载的方法进行比较。

String s1 = new String("lion");
String s2 = new String("lion");
System.out.println(s1.equals(s2)); // true
StringBuilder sb1 = new StringBuilder("lion");
StringBuilder sb2 = new StringBuilder("lion");
System.out.println(sb1.equals(sb2)); // false

这个应该是在StringBuilder类中equals没有重载,
因此用的是Object的equals方法来比较。
而String在重载的equals方法中,比较的是每个字符串的每个字符,所以是相等的。
- reflexive :反身性的,x.equals(x) 肯定是true
- symmetric: :对称的,如果x.equals(y)是true,那么y.equals(x)肯定也是true
- transitive: :传递性的,如果x.equals(y)是true,y.equals(z),那么 x.equals(z)也应该是相等的。
- consistent: : - x.equals(null) 永远都是false。而不是出NullPointerException

public boolean equals(Lion obj) {
    if (obj == null) return false;
    return this.idNumber == obj.idNumber;
}

传进来的参数如果不是Object类型,那么这个就不是Override,而是Overload。
虽然没有任何问题,但还是要注意

public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
}

重载之后比较的就是对象的所有成员变量。

public boolean equals(Object obj) {
    if ( !(obj instanceof LionEqualsBuilder)) return false;
    Lion other = (Lion) obj;
    return new EqualsBuilder().appendSuper(super.equals(obj))
                              .append(idNumber, other.idNumber)
                              .append(name, other.name)
                              .isEquals();
}

重载之后,比较的是对象中的idNumber和name。 虽然不是很简洁,还是得手动处理null和instanceof,但毕竟比所有都全手写要方便。

hashCode

当把object作为key存到map里去的话,会需要用到hashcode

public class Card {
    private String rank;
    private String suit;
    public Card(String r, String s) {
        if (r == null || s == null) {
            throw new IllegalArgumentException();
        }
        rank = r;
        suit = s;
    }
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Card)) {
            return false;
        }
        Card c = (Card) obj;
        return rank.equals(c.rank) && suit.equals(c.suit);
    }
    @Override
    public int hashCode() {
        return rank.hashCode();
    }
}

如果是primitive数字,可以直接用,或者取更小的int作为hashcode。
另外,一般在hashcode中不包含boolean和char类型的变量。

  • 同样的program,hashcode()的结果不会变。
    意味着不可以包含会变的变量。
    前面的Hippo类例子,只包括名字,因为体重会变。

  • 如果equals()的结果是true,那么hashCode()会返回相同的结果。
    这个意味着hashCode使用equals比较的成员变量的子集。
    例如上面Card的例子,有两个变量用于equals,但只有其中的一个用于hashCode。

  • 另一个角度讲,如果equals()的结果是false,hashCode()并不需要返回相同的结果。

//没问题,hashCode和equals用相同的变量
public int hashCode() { return idNumber; }
//没问题,但没有意义,永远返回相同的6
public int hashCode() { return 6; }
//方法名里的c不是C,因此是一个全新的方法。如果是C也有问题,override的时候返回值类型只能是子类。
public long hashcode() { return idNumber; }
//这个有问题,因为用到的参数比equals多,idNumber以外用了age
public int hashCode() { return idNumber * 7 + age; }

Working with Enums

public enum Season {
    WINTER, SPRING, SUMMER, FALL //没有分号
}

Enum值最后是没有分号的。
Enum和常数的区别在于,其是Type Safe,也就是在编译之前就知道这些成员变量的type。
enum包含的是static变量,以及helper method。

Season s = Season.SUMMER;

关于这个用法稍微有点违和感,貌似把类的成员变量赋予了类。

System.out.println(s == Season.SUMMER); // true

看上去像是两个字符串用等号来比较,有违和感。
可以想象成这是两个一开始就定义好的static final的变量。
这样用等号就没有违和感了。
enum的toString()起作用时,取得是enum的name

for(Season season: Season.values()) {
    System.out.println(season.name() + " " + season.ordinal());
}
System.out.println(Season.SPRING.name() + " " + Season.SPRING.ordinal());

这个看上去也有违和感。是因为Season的静态方法values()会取得其中的所有元素。
循环这个array,用name()方法取得每个元素的名字。用ordinal()方法,取得每个元素的值。

可以看出来每一个元素都有一个name一个顺序int值

Season s1 = Season.valueOf("SUMMER"); // SUMMER
Season s2 = Season.valueOf("summer"); // java.lang.IllegalArgumentException

可以用完全一模一样的字符串创建一个enum

Season.SPRING.values(); // Season的所有元素,少用
Season.values();        // Season的所有元素,相同

Using Enums in Switch Statements

Season summer = Season.SUMMER;
switch (summer) {
//这个用法是错的,因为:
//Season.WINTER是一个enum,并不是enum的int
//WINTER可以称为一个enum的value,是case可以接受的类型。
//Season可以称作enum的type。
case Season.WINTER:
    System.out.println("Get out the sled!");
    break;
case SUMMER:
    System.out.println("Time for the pool!");
    break;
default:
    System.out.println("Is it summer yet?");
}

最终针对Season.SUMMER了解几个概念:

Adding Constructors, Fields, and Methods

public enum Season {
    WINTER("Low"), SPRING("Medium"), SUMMER("High"), FALL("Medium");
    //成员变量
    private String expectedVisitors;
    //构造函数
    private Season(String expectedVisitors) {
        this.expectedVisitors = expectedVisitors;
    }
    //成员方法
    public void printExpectedVisitors() {
        System.out.println(expectedVisitors);
    }
}
  • 如果enum类里面只有value的一行,那么最后一个分号是可以省略的。
    像上面的情况,最后一个分号就不可以省略。
  • 构造方法的修饰词必须是private,因为只可以在enum之内被调用。public是无法通过编译的。
  • 构造函数可以看出来,是将方法的形参赋给成员变量。 再看第一行的定义就明白了,Low,Medium,High就是形参的具体值。
Season.SUMMER.printExpectedVisitors();

执行结果是High,因为SUMMER("High")调用构造函数把High赋给了SUMMER 另一enum的用法:

public enum Season {
    WINTER {
        @Override
        public void printHours() {
            System.out.println("9am-3pm");
        }
    },
    SPRING {
        @Override
        public void printHours() {
            System.out.println("9am-5pm");
        }
    },
    SUMMER {
        @Override
        public void printHours() {
            System.out.println("9am-7pm");
        }
    },
    FALL {
        @Override
        public void printHours() {
            System.out.println("9am-5pm");
        }
    };
    public abstract void printHours();
}
Season summer = Season.SUMMER;
summer.printHours();

Creating Nested Classes

嵌套类是定义在另一个类里面的类。
如果是非static,则叫做内部类inner class。

  • 成员内部类和成员变量同等级定义,非static。
    通常是指不显示定义类型的内部类。
  • 本地内部类,定义在方法体内。
  • 匿名内部类是一个没有名字的本地内部类的。
  • 静态嵌套类,是跟static变量同级别的静态类。

用嵌套类的好处之一:就是可以封装helper class到容器类中

Member Inner Classes

member level(和成员方法,成员变量和构造方法一级)定义的类。
- 可以定义,public,private,protected,default access
- 可以继承任何其他类或接口 - 可以是抽象类和final - 不可以定义静态成员和方法。 - 可以访问外部类的成员变量,包括private成员变量。 最后两条非常重要。

public class Outer {
    private String greeting = "Hi";
    protected class Inner {
        public int repeat = 3;
        public void go() {
            for (int i = 0; i < repeat; i++) {
                System.out.println(greeting);
            }
        }
    }
    public void callInner() {
        Inner inner = new Inner();
        inner.go();
    }
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.callInner();
        Outer outer2 = new Outer();
        // 一开始以为应该是 new outer2.Inner();
        // 但我错了,要记住这个格式
        Inner inner2 = outer2.new Inner();
    }
}

编译后的.class文件。一个是Outer.class,一个是Outer$Inner.class InnerClass可以和OuterClass有相同名字的成员变量。

public class A {
    private int x = 10;
    class B {
        private int x = 20;
        class C {
            private int x = 30;
            public void allTheX() {
                System.out.println(x);        // 30
                System.out.println(this.x);   // 30
                System.out.println(B.this.x); // 20
                System.out.println(A.this.x); // 10
            }
        }
    }
    public static void main(String[] args) {
        A a = new A();
        A.B b = a.new B();
        A.B.C c = b.new C();
        c.allTheX();
    }
}

可以从A.B省略成B,因为类B和main方法在同一个member level
但不可以把A.B.C省略成C,因为C在下一层。
另外可以增加一句import A.B.C,这样就可以直接用C了。

private interface
public class CaseOfThePrivateInterface {
    private interface Secret {
        public void shh();
    }
    class DontTell implements Secret {
        @Override
        public void shh() {
        }
    }
}

关于interface的规则没有变: - 成员方法缺省都是public,实装的方法必须是public的。
- interface本身不必须是public,跟内部类相同,inner interface可以是private的。
同时也意味着这个interface只能被outerclass使用。

Local Inner Classes

local inner class 定义直到方法被调用的时候才产生。
然后到方法返回时就失效了。
所有的动作跟成员变量相同。
- 不可以有修饰符。 - 不能声明为static,也不能有static的成员变量和方法。 - 可以访问包围类的所有成员和方法。 - 他们没有权限访问本地变量,除非这些变量是final或者等效final。

public class Outer {
    private int length = 5;
    public void calculate() {
        //这里是final所有没有问题
        //如果是 int width = 20; width = 30; 就会出错。
        final int width = 20;
        class Inner {

            public void multiply() {
                System.out.println(length * width);
            }
        }
        Inner inner = new Inner();
        inner.multiply();
    }
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.calculate();
    }
}

一个是instance变量,一个是final变量,因此在innerclass内可以访问。

effecitvely final

不需要显示定义,实际上只发生一次赋值的变量都是 effecitvely final

Anonymous Inner Classes

就是没有名字的Local Inner Class。

public class AnonInner {
    abstract class SaleTodayOnly {
        abstract int dollarsOff();
    }
    public int admission(int basePrice) {
        SaleTodayOnly sale = new SaleTodayOnly() {
            @Override
            int dollarsOff() {
                return 3;
            }
        };
        return basePrice - sale.dollarsOff();
    }
}

看上去像是new了一个抽象类,抽象类是不能被new的。
但只要有方法体{},就可以(多态?)
不是抽象类,接口也可以。 interface 的方法缺省是public的。
但抽象类的方法,是default的。
无法继承一个class的同时实现一个接口。
除非要继承的是java.lang.Object <-这句话什么意思。

如果真有这个需求,那么最好是定义成local inner class
然后然后就可以继承类和实现接口了。
如果真的需要这么复杂,内部类本身就可能并不是一个合适的选择。

public class AnonInner {
    interface SaleTodayOnly {
        int dollarsOff();
    }
    public int pay() {
        return admission(5, new SaleTodayOnly() {
            @Override
            public int dollarsOff() {
                return 3;
            }
        });
    }
    public int admission(int basePrice, SaleTodayOnly sale) {
        return basePrice - sale.dollarsOff();
    }
}

Static Nested Classes

静态类的用法跟静态方法一样,例如不能直接访问外部类的成员变量。

public class Enclosing {
    static class Nested {
        //静态类里面也可以定义静态方法,但是需要有静态类对象才能访问。
        private int price = 6;
    }
    public static void main(String[] args) {
        //初始化的时候不需要Enclosing的对象就可以初始化,这跟内部成员类有区别。
        Nested nested = new Nested();
        //生成了静态类的对象,然后访问。
        System.out.println(nested.price);
    }
}

import静态类时的两个方法。
普通方法: import Enclosing.Nested
静态导入: import static Enclosing.Nested

不是静态内部类,只能用普通方法import。
嵌套类import的时候,outerclass看上更像是一个namespace。

Summary

Exam Essentials

Review Questions

1Z0-809复习 第1章 toString(), equals(),hashCode(),

Advanced Class Design

Reviewing OCA Concepts

Access Modifiers

Overloading and Overriding

Abstract Classes

Static and Final

Imports

Using instanceof

Understanding Virtual Method Invocation

Annotating Overridden Methods

Coding equals, hashCode, and toString

java中所有类都是直接或间接继承了Object,因此也继承了Object类的所有方法。

toString

java中的print方法,会自动调用打印对象的toString()方法。

对象如果实装了toString()方法,使得打印的对象内容更清楚明了。
譬如ArrayList,[1,2,3]
如果对象使用Object的toString()方法,例如array对象
[Ljava.lang.String;@65cc892e]

一般的做法是打印出对象的所有成员变量,但一个一个写的话非常麻烦。
Apache社区提供了上述opensource,利用其提供的方法,
可以非常方便的打印出对象中所有成员变量。

//定义build.gradle
compile("org.apache.commons:commons-lang3:3.8")
//方法重载
public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

这其中利用到了反射,在实行期取得类的信息。
所有source:

import org.apache.commons.lang3.builder.ToStringBuilder;

public class Hippo {
    private String name;
    private double weight;
    public Hippo(String name, double weight) {
        this.name = name;
        this.weight = weight;
    }
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
    public static void main(String[] args) {
        Hippo h1 = new Hippo("Harry", 3100);
        System.out.println(h1); // Harry
    }
}

执行结果:com.apibot.Hippo@78308db1[name=Harry,weight=3100.0]

//简略版
public String toString() {
    return ToStringBuilder.reflectionToString(this,ToStringStyle.SHORT_PREFIX_STYLE);
}

执行结果:Hippo[name=Harry,weight=3100.0]

equals

如果类重载了equals方法,那么利用重载的方法进行比较。

String s1 = new String("lion");
String s2 = new String("lion");
System.out.println(s1.equals(s2)); // true
StringBuilder sb1 = new StringBuilder("lion");
StringBuilder sb2 = new StringBuilder("lion");
System.out.println(sb1.equals(sb2)); // false

这个应该是在StringBuilder类中equals没有重载,
因此用的是Object的equals方法来比较。
而String在重载的equals方法中,比较的是每个字符串的每个字符,所以是相等的。
- reflexive :反身性的,x.equals(x) 肯定是true
- symmetric: :对称的,如果x.equals(y)是true,那么y.equals(x)肯定也是true
- transitive: :传递性的,如果x.equals(y)是true,y.equals(z),那么 x.equals(z)也应该是相等的。
- consistent: : - x.equals(null) 永远都是false。而不是出NullPointerException

public boolean equals(Lion obj) {
    if (obj == null) return false;
    return this.idNumber == obj.idNumber;
}

传进来的参数如果不是Object类型,那么这个就不是Override,而是Overload。
虽然没有任何问题,但还是要注意

public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
}

重载之后比较的就是对象的所有成员变量。

public boolean equals(Object obj) {
    if ( !(obj instanceof LionEqualsBuilder)) return false;
    Lion other = (Lion) obj;
    return new EqualsBuilder().appendSuper(super.equals(obj))
                              .append(idNumber, other.idNumber)
                              .append(name, other.name)
                              .isEquals();
}

重载之后,比较的是对象中的idNumber和name。 虽然不是很简洁,还是得手动处理null和instanceof,但毕竟比所有都全手写要方便。

hashCode

当把object作为key存到map里去的话,会需要用到hashcode

public class Card {
    private String rank;
    private String suit;
    public Card(String r, String s) {
        if (r == null || s == null) {
            throw new IllegalArgumentException();
        }
        rank = r;
        suit = s;
    }
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Card)) {
            return false;
        }
        Card c = (Card) obj;
        return rank.equals(c.rank) && suit.equals(c.suit);
    }
    @Override
    public int hashCode() {
        return rank.hashCode();
    }
}

如果是primitive数字,可以直接用,或者取更小的int作为hashcode。
另外,一般在hashcode中不包含boolean和char类型的变量。

  • 同样的program,hashcode()的结果不会变。
    意味着不可以包含会变的变量。
    前面的Hippo类例子,只包括名字,因为体重会变。

  • 如果equals()的结果是true,那么hashCode()会返回相同的结果。
    这个意味着hashCode使用equals比较的成员变量的子集。
    例如上面Card的例子,有两个变量用于equals,但只有其中的一个用于hashCode。

  • 另一个角度讲,如果equals()的结果是false,hashCode()并不需要返回相同的结果。

//没问题,hashCode和equals用相同的变量
public int hashCode() { return idNumber; }
//没问题,但没有意义,永远返回相同的6
public int hashCode() { return 6; }
//方法名里的c不是C,因此是一个全新的方法。如果是C也有问题,override的时候返回值类型只能是子类。
public long hashcode() { return idNumber; }
//这个有问题,因为用到的参数比equals多,idNumber以外用了age
public int hashCode() { return idNumber * 7 + age; }

Working with Enums

public enum Season {
    WINTER, SPRING, SUMMER, FALL //没有分号
}

Enum值最后是没有分号的。
Enum和常数的区别在于,其是Type Safe,也就是在编译之前就知道这些成员变量的type。
enum包含的是static变量,以及helper method。

Season s = Season.SUMMER;

关于这个用法稍微有点违和感,貌似把类的成员变量赋予了类。

System.out.println(s == Season.SUMMER); // true

看上去像是两个字符串用等号来比较,有违和感。
可以想象成这是两个一开始就定义好的static final的变量。
这样用等号就没有违和感了。
enum的toString()起作用时,取得是enum的name

for(Season season: Season.values()) {
    System.out.println(season.name() + " " + season.ordinal());
}
System.out.println(Season.SPRING.name() + " " + Season.SPRING.ordinal());

这个看上去也有违和感。是因为Season的静态方法values()会取得其中的所有元素。
循环这个array,用name()方法取得每个元素的名字。用ordinal()方法,取得每个元素的值。

可以看出来每一个元素都有一个name一个顺序int值

Season s1 = Season.valueOf("SUMMER"); // SUMMER
Season s2 = Season.valueOf("summer"); // java.lang.IllegalArgumentException

可以用完全一模一样的字符串创建一个enum

Season.SPRING.values(); // Season的所有元素,少用
Season.values();        // Season的所有元素,相同

Using Enums in Switch Statements

Season summer = Season.SUMMER;
switch (summer) {
//这个用法是错的,因为:
//Season.WINTER是一个enum,并不是enum的int
//WINTER可以称为一个enum的value,是case可以接受的类型。
//Season可以称作enum的type。
case Season.WINTER:
    System.out.println("Get out the sled!");
    break;
case SUMMER:
    System.out.println("Time for the pool!");
    break;
default:
    System.out.println("Is it summer yet?");
}

最终针对Season.SUMMER了解几个概念:

Adding Constructors, Fields, and Methods

public enum Season {
    WINTER("Low"), SPRING("Medium"), SUMMER("High"), FALL("Medium");
    //成员变量
    private String expectedVisitors;
    //构造函数
    private Season(String expectedVisitors) {
        this.expectedVisitors = expectedVisitors;
    }
    //成员方法
    public void printExpectedVisitors() {
        System.out.println(expectedVisitors);
    }
}
  • 如果enum类里面只有value的一行,那么最后一个分号是可以省略的。
    像上面的情况,最后一个分号就不可以省略。
  • 构造方法的修饰词必须是private,因为只可以在enum之内被调用。public是无法通过编译的。
  • 构造函数可以看出来,是将方法的形参赋给成员变量。 再看第一行的定义就明白了,Low,Medium,High就是形参的具体值。
Season.SUMMER.printExpectedVisitors();

执行结果是High,因为SUMMER("High")调用构造函数把High赋给了SUMMER 另一enum的用法:

public enum Season {
    WINTER {
        @Override
        public void printHours() {
            System.out.println("9am-3pm");
        }
    },
    SPRING {
        @Override
        public void printHours() {
            System.out.println("9am-5pm");
        }
    },
    SUMMER {
        @Override
        public void printHours() {
            System.out.println("9am-7pm");
        }
    },
    FALL {
        @Override
        public void printHours() {
            System.out.println("9am-5pm");
        }
    };
    public abstract void printHours();
}
Season summer = Season.SUMMER;
summer.printHours();

Creating Nested Classes

Member Inner Classes

Local Inner Classes

Anonymous Inner Classes

Static Nested Classes

Summary

Exam Essentials

Review Questions

1Z0-809复习 第1章 ENUM值

Advanced Class Design

Reviewing OCA Concepts

Access Modifiers

Overloading and Overriding

Abstract Classes

Static and Final

Imports

Using instanceof

Understanding Virtual Method Invocation

Annotating Overridden Methods

Coding equals, hashCode, and toString

toString

equals

hashCode

Working with Enums

public enum Season {
    WINTER, SPRING, SUMMER, FALL //没有分号
}

Enum值最后是没有分号的。
Enum和常数的区别在于,其是Type Safe,也就是在编译之前就知道这些成员变量的type。
enum包含的是static变量,以及helper method。

Season s = Season.SUMMER;

关于这个用法稍微有点违和感,貌似把类的成员变量赋予了类。

System.out.println(s == Season.SUMMER); // true

看上去像是两个字符串用等号来比较,有违和感。
可以想象成这是两个一开始就定义好的static final的变量。
这样用等号就没有违和感了。
enum的toString()起作用时,取得是enum的name

for(Season season: Season.values()) {
    System.out.println(season.name() + " " + season.ordinal());
}
System.out.println(Season.SPRING.name() + " " + Season.SPRING.ordinal());

这个看上去也有违和感。是因为Season的静态方法values()会取得其中的所有元素。
循环这个array,用name()方法取得每个元素的名字。用ordinal()方法,取得每个元素的值。

可以看出来每一个元素都有一个name一个顺序int值

Season s1 = Season.valueOf("SUMMER"); // SUMMER
Season s2 = Season.valueOf("summer"); // java.lang.IllegalArgumentException

可以用完全一模一样的字符串创建一个enum

Season.SPRING.values(); // Season的所有元素,少用
Season.values();        // Season的所有元素,相同

Using Enums in Switch Statements

        Season summer = Season.SUMMER;
        switch (summer) {
        //这个用法是错的,因为:
        //Season.WINTER是一个enum,并不是enum的int
        //WINTER可以称为一个enum的value,是case可以接受的类型。
        //Season可以称作enum的type。
        case Season.WINTER:
            System.out.println("Get out the sled!");
            break;
        case SUMMER:
            System.out.println("Time for the pool!");
            break;
        default:
            System.out.println("Is it summer yet?");
        }

最终针对Season.SUMMER了解几个概念:

Adding Constructors, Fields, and Methods

public enum Season {
    WINTER("Low"), SPRING("Medium"), SUMMER("High"), FALL("Medium");
    //成员变量
    private String expectedVisitors;
    //构造函数
    private Season(String expectedVisitors) {
        this.expectedVisitors = expectedVisitors;
    }
    //成员方法
    public void printExpectedVisitors() {
        System.out.println(expectedVisitors);
    }
}
  • 如果enum类里面只有value的一行,那么最后一个分号是可以省略的。
    像上面的情况,最后一个分号就不可以省略。
  • 构造方法的修饰词必须是private,因为只可以在enum之内被调用。public是无法通过编译的。
  • 构造函数可以看出来,是将方法的形参赋给成员变量。 再看第一行的定义就明白了,Low,Medium,High就是形参的具体值。
Season.SUMMER.printExpectedVisitors();

执行结果是High,因为SUMMER("High")调用构造函数把High赋给了SUMMER 另一enum的用法:

public enum Season {
    WINTER {
        @Override
        public void printHours() {
            System.out.println("9am-3pm");
        }
    },
    SPRING {
        @Override
        public void printHours() {
            System.out.println("9am-5pm");
        }
    },
    SUMMER {
        @Override
        public void printHours() {
            System.out.println("9am-7pm");
        }
    },
    FALL {
        @Override
        public void printHours() {
            System.out.println("9am-5pm");
        }
    };
    public abstract void printHours();
}
        Season summer = Season.SUMMER;
        summer.printHours();

Creating Nested Classes

Member Inner Classes

Local Inner Classes

Anonymous Inner Classes

Static Nested Classes

Summary

Exam Essentials

Review Questions

1z-808复习 第5章

Class Design

  • Describe inheritance and its benefits
  • Develop code that demonstrates the use of polymorphism;
    including overriding and object type versus reference type
    开发代码来示范多态
  • Determine when casting is necessary
  • Use super and this to access objects and constructors
  • Use abstract classes and interfaces

Introducing Class Inheritance

子类继承父类所有的public,protected变量。
子类/派生类:child class或者是descendent of the class
父类:parent class或者是ancestor of the class
single inheritance:只可以继续一个父类,但可以实现多个接口
multiple levels of inheritance: 子类继承父类,父类继承父类的父类,多层次继承。

Extending a Class

每个文件里最多只能有一个public class,也可以没有public class。

Applying Class Access Modifiers

protected 和 private 只能用于inner class

Creating Java Objects

只有一个概念,如果没有明确指定继承的父类,就会缺省继承Object,因此所有的类最终都是继承的Object

Defining Constructors

this()和super()要放在第一行

Understanding Compiler Enhancements

如果构造函数中没有明确调用父类构造函数java会自动插入一条super()的父类构建函数

Reviewing Constructor Rules
  1. 每个构建方法的第一行代码,是用this()来调用这个类里的另一个构建方法。
    或者是调用直接父类的super()。
  2. 同样super()也不能在构建方法中放在第二行以后。(必须在第一行)
  3. 如果在构建函数中不存在super(),Java会自动插入一个没有参数的super(),作为构建函数的第一行。
  4. 根据第3条,如果父类中不存在没有参数的构建函数,并且子类中没有定义任何构建方法,
    编译器会报错,因为它自动往子类里生成一个空的构建函数,该构建函数自动调用父类的空的构建函数
  5. 如果父类中不存在没有参数的构建函数,那么在子类的每个构建函数中都要明确调用父类的构建函数

Calling Constructors

父类的super比this优先调用

Calling Inherited Class Members

子类可以使用父类的public或者protected成员。
如果子类和父类在同一package中,那么子类可以使用父类所有的default成员。
this.member来调用子类。
super.member来调用父类。

Inheriting Methods

Overriding a Method

如果override一个非private的父类方法,编译器会自动进行以下check:

  1. 首先method signature必须相同,即方法名和方法参数必须完全相同。
  2. 子类的 access level必须要比父类broader。
  3. 子类的方法不可以抛出一个新的checked exception,也不可以抛出broader exception(父类Exception)
  4. 方法的放回类型必须是父类返回类型的同类或子类。这个叫covariant
Redeclaring private Methods

如果是private函数,同样的method signature,相当于声明了一个完全新的函数
所以上面的规则完全不适用,也就是说private方法是没有override的。

Hiding Static Methods

对于静态方法,没有override,只有hidden。
比override多一条check:如果父类是static,子类必须也是static。
如果父类不是static,那么子类也不可以是static。

Overriding vs. Hiding Methods

在执行时,如果是instance method ,那么child version总是被执行。
除非明确是父类的reference调用发

Creating final methods

加了final的method是不能被继承的。

Defining an Interface

接口基本上可以看做是一个抽象类。

  1. 接口不能直接实例化。
  2. An interface is not required to have any methods.
    英文水平问题,接口并不是要求一定要有方法,误认为是接口不能有任何方法。
  3. 接口需要被继承,当然不能是final的。
  4. 所有顶层接口应该是public或default的, 且在其定义中必须包含abstract <- 在编译完之后会自动加上pubic
    问题是这样的话,default就没有意义了,会被自动转成public才对
    接口的定义如果是private, protected, or final,那么就会出编译错误。
    接口定义的修饰词中,只有public以及abstract被允许
  5. 所有非default方法被假定有abstract和public在定义中。 同样因此,他们不能是private, protected, or final

只有abstract的时候,看说明似乎是不会自动加上public,因为可以是default。 但例子又是自动加上的。

Inheriting an Interface

  • 一个借口extends另一个接口,和abstract class implements一个interface相同,
    所有的abstract method都会被继承

  • 第一个实体类,实现了接口或者抽象类,必须对所有继承的abstract 方法进行实体化。

和抽象类一样,一类接口可以继承另外一个接口。它会将另一个接口的所有抽象方法都继承过来。
和抽象诶不同的是,一个接口可以extends多个接口

Classes, Interfaces, and Keywords

多重继承的两个接口中,如果定义的函数签名完全相同的方法,则不冲突。 如果方法名相同,只是参数不同,那是overloading,两个方法都实体化就可以了。 如果方法名相同,参数也相同,只是返回值不同。则会出编译错误。

Interface Variables

接口变量默认为是public static final的

Ploymophic Parameters One of themost useful applications of ploymorphism is the ability
to pass instance of a subclass

Inheriting Variables

变量没有override,只有hidden。

Creating Abstract Classes

抽象类是一个必须有abstract关键字的类,并且其不可以被实例化。
抽象方法是定义在抽象类中,有abstract关键字的方法。

Defining an Abstract Class

抽象类里面可以放非抽象的方法,和变量。
抽象类可以不包含任何抽象方法。
抽象方法必须定义在抽象类中。
抽象方法不能有方法体{}。

如何在抽象类中为抽象方法提供缺省的实装?

抽象类和方法不能是final。
抽象方法不能是private。

Creating a Concrete Class

主要是在extends抽象类时,必须实装其中的抽象方法。

Extending an Abstract Class

如果是抽象类继承抽象类,那不需要实装抽象方法。
如果是实体化抽象类,那么就需要实装所有的抽象方法。

抽象类定义规则:

  1. 抽象类不能直接实例化
  2. 抽象类中可以有任何数量的抽象方法或者非抽象方法。
  3. 抽象类不可以是final或者是private
  4. 继承抽象类,也就是继承了所有抽象方法。
  5. 第一个实例化的类必须实装所有的抽象方法。

抽象方法定义规则: 1. 抽象方法必须定义在抽象类中 2. 抽象方法不能是private或是final 3. 抽象方法在声明的类中不能有方法体或实装 4. 实装抽象方法需要满足所有override的规则

Implementing Interfaces

接口是定义了public abstract方法和数据类型。
接口中定义了常量, public static final
接口中定义了缺省的方法。

Understanding Polymorphism

Summary

Exam Essentials

Review Questions

1.BE
2.B
3.AD
4.

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