ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

on java8学习笔记2022.2.19-2022.2.20

2022-02-20 23:33:33  阅读:171  来源: 互联网

标签:20 String 19 void 接口 class new 2022.2 public


2022.2.19

第十章 接口

  1. 如果一个类并不需要包含抽象方法,但同时还想阻止对它的任何实例化,这时将其定义为抽象类就很有用了。

  2. 接口和抽象类之间最显著的区别可能是两者的惯用方式。接口通常暗示“类的类型”或作为形容词来使用,例如RunnableSerializable,而抽象类通常是类层次结构的一部分,并且是“事物的类型”,例如StringInstrument

  3. 接口也可以包含字段,但这些字段是隐式的staticfinal

  4. 接口(该接口的修饰符是默认)如果只有包访问权限的话,那么包外的类是无法实现这个接口的,即便这个接口里是public方法

  5. 你可以选择将接口中的方法显式声明为public,但即使不显式声明,它们也是public的。所以当实现一个接口时,来自接口的方法必须被定义为public。否则,它们将默认为包访问权限,导致在继承期间降低了方法的可访问性,而这是Java编译器不允许的。

    其实这里我是没太理解作者的意思的,不过就我目前测试的结果,其实接口直接默认确实是最好的选择,因为接口没有private这种东西,也没有protected这种东西,实际上就是默认和public,而当接口是public时,它的方法本身就全部默认是public的,而默认的时候就全部内置默认,非常和谐,而接口本身就是为了描述类所共有的特征的,所有类的方法的这么个东西,所以访问权限一致我倒是觉得挺合理的

    package Test;
    
    import example.a;
    import example.one;
    
    public class test implements a {
        public static void main(String[] args){
            new test().function();
        }
        public void function(){
            System.out.println("hello");
        }
    }
    
    package example;
    
    public interface a {
        void function();
    }
    
  6. 添加默认方法的一个令人信服的原因是,它允许向现有接口中添加方法,而不会破坏已经在使用该接口的所有代码。默认方法有时也称为防御方法(defender method)或虚拟扩展方法(virtual extension method)。

    在JDK 9中,接口里的defaultstatic方法都可以是private的。

    讲道理,第二句话就使得接口有了类的特征了,而且很诡异,这么搞有什么意义,如果设为了private,接口外的都无法访问这个方法,那我设置这个方法是要干嘛?注意,我的意思是这个接口对实现类是直接隐藏的,因为不是有默认final这个东西,真的让人头大,比如下面这个

    package example;
    
    public interface a {
        private void b(){
            System.out.println("hello");
        }
    
    }
    
    package example;
    public class one implements a{
        String s1 = "hello";
        public one (String s1){
            this.s1 = s1;
        }
        public static void main(String[] args){
            System.out.println(new two("second"));
            a b = new one();
            b.
        }
        @Override
        private void b(){
    
        }
    }
    

    关注下@Override,会发现,它提示你未从超类重写方法,而下面的代码显示了你在包外无法调用接口a的私有静态方法,这导致我看不懂这个私有静态方法有什么用,当然,同时编译器也会告诉你接口中的方法多余.

    package example;
    public class one implements a{
        String s1 = "hello";
        public one (String s1){
            this.s1 = s1;
        }
        public static void main(String[] args){
            a.c();
        }
    }
    
    package example;
    
    public interface a {
        private static void b(){
            System.out.println("hello");
        }
        public static void c(){
            System.out.println("hi");
        }
    }
    
  7. // interfaces/MultipleInheritance.java
    import java.util.*;
    
    interface One {
    default void first() { System.out.println("first"); }
    }
    
    interface Two {
    default void second() {
    System.out.println("second");
    }
    }
    
    interface Three {
    default void third() { System.out.println("third"); }
    }
    
    class MI implements One, Two, Three {}
    
    public class MultipleInheritance {
    public static void main(String[] args) {
    MI mi = new MI();
    mi.first();
    mi.second();
    mi.third();
    }
    }
    /* 输出:
    first
    second
    third
    */
    
    // interfaces/MICollision.java
    import java.util.*;
    
    interface Bob1 {
    default void bob() {
    System.out.println("Bob1::bob");
    }
    }
    
    interface Bob2 {
    default void bob() {
    System.out.println("Bob2::bob");
    }
    }
    
    // class Bob implements Bob1, Bob2 {}
    /* 产生:
    error: class Bob inherits unrelated defaults
    for bob() from types Bob1 and Bob2
    class Bob implements Bob1, Bob2 {}
    ^
    1 error
    */
    
    interface Sam1 {
    default void sam() {
    System.out.println("Sam1::sam");
    }
    }
    
    interface Sam2 {
    default void sam(int i) {
    System.out.println(i * 2);
    }
    }
    
    // 这里能正常工作是因为参数列表不同
    class Sam implements Sam1, Sam2 {}
    
    interface Max1 {
    default void max() {
    System.out.println("Max1::max");
    }
    }
    
    interface Max2 {
    default int max() { return 47; }
    }
    
    // class Max implements Max1, Max2 {}
    /* 产生:
    error: types Max2 and Max1 are incompatible;
    both define max(), but with unrelated return types
    class Max implements Max1, Max2 {}
    ^
    1 error
    */
    

    这里要解决的话就只能重写

  8. 返回类型不是方法签名的一部分

  9. // interfaces/Jim.java
    import java.util.*;
    
    interface Jim1 {
      default void jim() {
        System.out.println("Jim1::jim");
      }
    }
    
    interface Jim2 {
      default void jim() {
        System.out.println("Jim2::jim");
      }
    }
    
    public class Jim implements Jim1, Jim2 {
      @Override public void jim() {
        Jim2.super.jim();
      }
      public static void main(String[] args) {
        new Jim().jim();
      }
    }
    /* 输出:
    Jim2::jim
    */
    

    不是很清楚为什么这里一定要加个super,如果不加的话,会报错,当然,也可以这么写

    package example;
    // interfaces/Jim.java
    import java.util.*;
    
    interface Jim1 {
        default void jim() {
            System.out.println("Jim1::jim");
        }
    }
    
    interface Jim2 {
        default void jim() {
            System.out.println("Jim2::jim");
        }
    }
    
    public class Jim implements Jim1, Jim2 {
        @Override public void jim() {
            System.out.println("hello");
        }
        public static void main(String[] args) {
            new Jim().jim();
        }
    }
    

    有可能那个交给它基类写去了?谁知道呢

  10. // interfaces/MetalWork.java
    import onjava.Operation;
    
    class Heat implements Operation {
      @Override public void execute() {
        Operation.show("Heat");
      }
    }
    
    public class MetalWork {
      public static void main(String[] args) {
        // 必须在静态上下文中定义才能使用方法引用
        Operation twist = new Operation() {
          public void execute() {
            Operation.show("Twist");
          }
        };
        Operation.runOps(
          new Heat(),                     // [1]
          new Operation() {               // [2]
            public void execute() {
              Operation.show("Hammer");
            }
          },
          twist::execute,                 // [3]
          () -> Operation.show("Anneal")  // [4]
        );
      }
    }
    /* 输出:
    Heat
    Hammer
    Twist
    Anneal
    */
    
    // onjava/Operation.java
    package onjava;
    
    public interface Operation {
      void execute();
      static void runOps(Operation... ops) {
        for(Operation op : ops)
          op.execute();
      }
      static void show(String msg) {
        System.out.println(msg);
      }
    }
    

    这个看了挺久的,除了最后一个lambda没学过以外,其他大概看懂了,第一个是上面已经定义好的类,第二个是以接口定义的类,第三个是方法引用.

    这里让我比较新奇的就是匿名的接口类的定义方式了

    怎么看这个匿名接口类的定义方法呢?首先将new先隔开,那么后面Operation就是定义接口中需要我们定义的方法,而那个new则是创建这个对象

    Operation() {
          public void execute() {
            Operation.show("Twist");
          }
        }
    

    这里没有构造器,应该调用的默认构造器

     {
          public void execute() {
            Operation.show("Twist");
          }
        }
    
> ```java
> // interfaces/interfaceprocessor/Processor.java
> package interfaces.interfaceprocessor;
> 
> public interface Processor {
> default String name() {
>  return getClass().getSimpleName();
> }
> Object process(Object input);
> }
> // interfaces/interfaceprocessor/Applicator.java
> package interfaces.interfaceprocessor;
> 
> public class Applicator {
> public static void apply(Processor p, Object s) {
>  System.out.println("Using Processor " + p.name());
>  System.out.println(p.process(s));
> }
> }
> ```
>
> 复用代码的第一种方法是,调用者可以编写符合这个接口的类,如下所示:
>
> ```java
> // interfaces/interfaceprocessor/StringProcessor.java
> // {java interfaces.interfaceprocessor.StringProcessor}
> package interfaces.interfaceprocessor;
> import java.util.*;
> 
> interface StringProcessor extends Processor {
> @Override
> String process(Object input);        // [1]
> String S =                           // [2]
> "If she weighs the same as a duck, " +
> "she's made of wood";
> static void main(String[] args) {    // [3]
>  Applicator.apply(new Upcase(), S);
>  Applicator.apply(new Downcase(), S);
>  Applicator.apply(new Splitter(), S);
> }
> }
> 
> class Upcase implements StringProcessor {
> @Override // 协变返回
> public String process(Object input) {
>  return ((String)input).toUpperCase();
> }
> }
> 
> class Downcase implements StringProcessor {
> @Override
> public String process(Object input) {
>  return ((String)input).toLowerCase();
> }
> }
> 
> class Splitter implements StringProcessor {
> @Override
> public String process(Object input) {
>  return Arrays.toString(((String)input).split(" "));
> }
> }
> /* 输出:
> Using Processor Upcase
> IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD
> Using Processor Downcase
> if she weighs the same as a duck, she's made of wood
> Using Processor Splitter
> [If, she, weighs, the, same, as, a, duck,, she's, made,
> of, wood]
> */
> ```
>
> [1] 这个声明是不必要的,如果删除它,编译器也不会提示错误。但它能指出方法的返回值从`Object`协变为`String`。
> [2] 字段`s`自动是`static`和`final`的,因为它是在接口内定义的。
> [3] 你甚至可以在接口中定义一个`main()`方法。

接口继承接口,我只能说好家伙,还有一个默认的main()方法,真是把我秀到了

```java
package example;

public interface a {
    private static void b(){
        System.out.println("hello");
    }
    static void c(){
        System.out.println("hi");
    }
    static void main(String[] args) {
        b();
    }
}
```

```java
package example;
public class one implements a{
    String s1 = "hello";
    public one (String s1){
        this.s1 = s1;
    }
    public static void main(String[] args){
        a.c();
        a.main(args);
    }
}
```

忽然间我就懂怎么用这个诡异的private的方法了,把他嵌套再这个接口的其他public方法里
  1. // interfaces/interfaceprocessor/FilterProcessor.java
    // {java interfaces.interfaceprocessor.FilterProcessor}
    package interfaces.interfaceprocessor;
    import interfaces.filters.*;
    
    class FilterAdapter implements Processor {
    Filter filter;
    FilterAdapter(Filter filter) {
     this.filter = filter;
    }
    @Override
    public String name() { return filter.name(); }
    @Override
    public Waveform process(Object input) {
     return filter.process((Waveform)input);
    }
    }
    
    public class FilterProcessor {
    public static void main(String[] args) {
     Waveform w = new Waveform();
     Applicator.apply(
       new FilterAdapter(new LowPass(1.0)), w);
     Applicator.apply(
       new FilterAdapter(new HighPass(2.0)), w);
     Applicator.apply(
       new FilterAdapter(new BandPass(3.0, 4.0)), w);
    }
    }
    /* 输出:
    Using Processor LowPass
    Waveform 0
    Using Processor HighPass
    Waveform 0
    Using Processor BandPass
    Waveform 0
    */
    

    在使用适配器的实现方式里,FilterAdapter构造器通过你拥有的Filter接口,来生成一个你需要的Processor接口的对象。你可能还会注意到FilterAdapter类中使用了委托。

    协变允许我们从process()里产生一个Waveform,而不仅仅是一个Object

    接口与实现的解耦允许我们将一个接口应用于多个不同的实现,因此代码更具可复用性。

    挺骚的,我现在看委托越来越感觉像是在玩代理

  2. // interfaces/Adventure.java
    // Multiple interfaces
    
    interface CanFight {
      void fight();
    }
    
    interface CanSwim {
      void swim();
    }
    
    interface CanFly {
      void fly();
    }
    
    class ActionCharacter {
      public void fight() {}
    }
    
    class Hero extends ActionCharacter
        implements CanFight, CanSwim, CanFly {
      @Override public void swim() {}
      @Override public void fly() {}
    }
    
    public class Adventure {
      public static void t(CanFight x) { x.fight(); }
      public static void u(CanSwim x) { x.swim(); }
      public static void v(CanFly x) { x.fly(); }
      public static void w(ActionCharacter x) { x.fight(); }
      public static void main(String[] args) {
        Hero h = new Hero();
        t(h); // 当作一个CanFight类型
        u(h); // 当作一个CanSwim类型
        v(h); // 当作一个CanFly类型
        w(h); // 当作一个ActionCharacter类型
      }
    }
    

    发现我搞混了一个东西,如果是下面这种形式

    public void fun(){}
    

    上面这种表示的是一个默认方法,也就是他不是abstract的,下面这种才是抽象方法的写法

    public void fun();
    
  3. 这就带来了一个问题:应该使用接口还是抽象类?如果可以在没有任何方法定义或成员变量的情况下创建基类,那么就使用接口而非抽象类。事实上,如果你认为某个类可以作为基类的话,也就可以考虑把它设计成接口(在10.11节中会重新讨论这个主题)。\

  4. // interfaces/HorrorShow.java
    // 通过继承来扩展接口
    
    interface Monster {
      void menace();
    }
    
    interface DangerousMonster extends Monster {
      void destroy();
    }
    
    interface Lethal {
      void kill();
    }
    
    class DragonZilla implements DangerousMonster {
      @Override public void menace() {}
      @Override public void destroy() {}
    }
    
    interface Vampire extends DangerousMonster, Lethal {
      void drinkBlood();
    }
    
    class VeryBadVampire implements Vampire {
      @Override public void menace() {}
      @Override public void destroy() {}
      @Override public void kill() {}
      @Override public void drinkBlood() {}
    }
    
    public class HorrorShow {
      static void u(Monster b) { b.menace(); }
      static void v(DangerousMonster d) {
        d.menace();
        d.destroy();
      }
      static void w(Lethal l) { l.kill(); }
      public static void main(String[] args) {
        DangerousMonster barney = new DragonZilla();
        u(barney);
        v(barney);
        Vampire vlad = new VeryBadVampire();
        u(vlad);
        v(vlad);
        w(vlad);
      }
    }
    

    注意下,这里Vampire一次性继承了大量的接口,但是,这是接口特有的属性,类不能这样

  5. // interfaces/InterfaceCollision.java
    
    interface I1 { void f(); }
    interface I2 { int f(int i); }
    interface I3 { int f(); }
    class C { public int f() { return 1; } }
    
    class C2 implements I1, I2 {
    @Override
    public void f() {}
    @Override
    public int f(int i) { return 1; } // 重载
    }
    
    class C3 extends C implements I2 {
    @Override
    public int f(int i) { return 1; } // 重载
    }
    
    class C4 extends C implements I3 {
    // 完全相同,没有问题:
    @Override public int f() { return 1; }
    }
    
    // 方法只有返回类型不同
    //- class C5 extends C implements I1 {}
    //- interface I4 extends I1, I3 {}
    

    之所以出现问题,是因为重写、实现和重载令人不快地混合在一起。此外,重载方法不能只有返回类型不同。当最后两行取消注释时,错误消息说明了一切:

    error: C5 is not abstract and does not override abstract
    method f() in I1
    class C5 extends C implements I1 {}
    
    error: types I3 and I1 are incompatible; both define f(),
    but with unrelated return types
    interface I4 extends I1, I3 {}
    

    这里我还是有点意外的,原来重载方法只有返回类型不同真会报错,我把这个改成了下面这个就不会报错了

    // interfaces/InterfaceCollision.java
    
    interface I1 { void f(); }
    interface I2 { int f(int i); }
    interface I3 { int f(); }
    class C { public int f() { return 1; } }
    
    class C2 implements I1, I2 {
        @Override
        public void f() {}
        @Override
        public int f(int i) { return 1; } // 重载
    }
    
    class C3 extends C implements I2 {
        @Override
        public int f(int i) { return 1; } // 重载
    }
    
    class C4 extends C implements I3 {
        // 完全相同,没有问题:
        @Override public int f() { return 1; }
    }
    
    // 方法只有返回类型不同
    //class C5 extends C implements I1 {}
    interface I4 extends I1, I2 {}
    

    然后我这里思考了一下,参数全部一样,返回类型居然不一样确实很离谱,这就不就变成抽奖了,而且你这样实现也会很为难,让人无法理解

  6. 下面代码展示了final修饰的变量必须被初始化,否则报错

    package example;
    public final class one implements a{
        public final int i ;
        public String s1 = "hello";
        public one (String s1){
            this.s1 = s1;
        }
        public static void main(String[] args){
            one a = new one("fhdjahj");
            a.s1="hello world";
            System.out.println(a.i);
        }
    }
    

    只要将final去掉,那么输出结果就是0,否则你编译都过不去

2022.2.20

第十章 接口

  1. 接口中的任何字段都自动是staticfinal的,因此接口是创建一组常量值的便捷工具。

  2. package Test;
    
    import example.a;
    import example.one;
    import example.*;
    public class test  {
        public static void main(String[] args){
            //new test().function();
            System.out.println(Months.JANUARY);
        }
        public void function(){
            System.out.println("hello");
        }
    }
    
    package example;
    
    public interface Months {
     int
             JANUARY = 1, FEBRUARY = 2, MARCH = 3,
             APRIL = 4, MAY = 5, JUNE = 6, JULY = 7,
             AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10,
             NOVEMBER = 11, DECEMBER = 12;
    }
    

    首先要想这么用得先导包,我提前用idea,怎么导Months都不对,所以把他放进了我自己创建的包里面去了,然后我实验了一下,确实已经被默认修改成了final

    package Test;
    
    import example.a;
    import example.one;
    import example.*;
    public class test  {
        public static void main(String[] args){
            //new test().function();
            Months.JANUARY=2;
        }
        public void function(){
            System.out.println("hello");
        }
    }
    
  3. 这些字段不是接口的一部分。这些值存储在该接口的静态存储区中。

  4. 接口可以嵌套在类和其他接口中。这呈现了几个有趣的特性:

    // interfaces/nesting/NestingInterfaces.java
    // {java interfaces.nesting.NestingInterfaces}
    package interfaces.nesting;
    
    class A {
    interface B {
     void f();
    }
    public class BImp implements B {
     @Override public void f() {}
    }
    private class BImp2 implements B {
     @Override public void f() {}
    }
    public interface C {
     void f();
    }
    class CImp implements C {
     @Override public void f() {}
    }
    private class CImp2 implements C {
     @Override public void f() {}
    }
    private interface D {
     void f();
    }
    private class DImp implements D {
     @Override public void f() {}
    }
    public class DImp2 implements D {
     @Override public void f() {}
    }
    public D getD() { return new DImp2(); }
    private D dRef;
    public void receiveD(D d) {
     dRef = d;
     dRef.f();
    }
    }
    
    interface E {
    interface G {
     void f();
    }
    // 多余的public:
    public interface H {
     void f();
    }
    void g();
    // 接口内不能用private
    //- private interface I {}
    }
    
    public class NestingInterfaces {
    public class BImp implements A.B {
     @Override public void f() {}
    }
    class CImp implements A.C {
     @Override public void f() {}
    }
    // private的接口只能在定义的类里实现:
    //- class DImp implements A.D {
    //-  public void f() {}
    //- }
    class EImp implements E {
     @Override public void g() {}
    }
    class EGImp implements E.G {
     @Override public void f() {}
    }
    class EImp2 implements E {
     @Override public void g() {}
     class EG implements E.G {
       @Override public void f() {}
     }
    }
    public static void main(String[] args) {
     A a = new A();
     // 无法访问A.D:
     //- A.D ad = a.getD();
     // 只能返回A.D:
     //- A.DImp2 di2 = a.getD();
     // 无法访问该接口的方法
     //- a.getD().f();
     // 另一个A才能处理getD():
     A a2 = new A();
     a2.receiveD(a.getD());
    }
    }
    

    在类中嵌套接口的语法相当明显。就像非嵌套接口一样,它们可以具有public或包访问权限的可见性。

    另外这里有个不易理解之处:接口也可以是private的,如A.D中所示(嵌套接口和嵌套类使用相同的限定语法)。private的嵌套接口有什么好处?你可能会猜测,它只能作为DImp中的私有内部类来实现,但是A.DImp2表明它也可以作为public类来实现。不过A.DImp2在使用时只能被视为自身的类型,你不能提及它实现了private的接口D。所以实现了private接口的话,可以在不添加任何类型信息的情况下,限定该接口中的方法定义(也就是说,不允许任何向上转型)。

    方法getD()让我们进一步陷入困境,这与private接口有关:它是一个public方法,但返回了一个private接口的引用。可以对这个方法的返回值做些什么?在main()里我们多次尝试使用该返回值,但都失败了。该返回值必须传递给一个有权使用它的对象——这里是另一个A,它可以通过receiveD()方法使用这个返回值。

    接口E表明,接口之间也可以嵌套。然而,关于接口的规则——特别是所有接口元素必须是public的——在这里是严格执行的,所以嵌套在另一个接口中的接口自动为public的,不能设为private

    NestingInterfaces展示了实现嵌套接口的各种方式。特别要注意的是,当实现一个接口时,并不需要实现嵌套在其中的接口。此外,private接口不能在它们的定义类之外实现。

    初看起来,这些特性好像仅仅是为了语法一致性而添加的,但我通常发现,一旦了解了一个特性,你就会经常发现它的有用之处。

    看了很久,D那里应该说的是因为接口D是private的,也就是只在类A中可见,那么你就不能写成这个形式了

    A.D a = new Dimp();
    

    很有意思的一点,一个默认类里面居然定义了一个公共类,不过我试了下,这个公共类实际上还是出不了包

    package Test;
    
    import example.a;
    import example.one;
    import example.*;
    public class test  {
        public static void main(String[] args){
            //new test().function();
            System.out.println(new three.four());
            
        }
        public void function(){
            System.out.println("hello");
        }
    }
    
    package example;
    public final class one implements a{
        public int i ;
        public String s1 = "hello";
        public one (String s1){
            this.s1 = s1;
        }
        public static void main(String[] args){
            one a = new one("fhdjahj");
            a.s1="hello world";
            System.out.println(a.i);
        }
    }
    class three {
    
        public class four {
            String s1 = "hello world";
            static{
                System.out.println("hello");
            }
            public String toString(){
                return s1;
            }
        }
    }
    
    package example;
    public final class one implements a{
        public int i ;
        public String s1 = "hello";
        public one (String s1){
            this.s1 = s1;
        }
        public static void main(String[] args){
            one a = new one("fhdjahj");
            a.s1="hello world";
            System.out.println(a.i);
        }
    }
    class three {
    
        public class four {
            String s1 = "hello world";
            static{
                System.out.println("hello");
            }
            public String toString(){
                return s1;
            }
        }
    }
    
  5. 看了这么多接口的内容,现在就感觉接口就像游戏中的法师,如果要调用火元素来进行攻击,就必须动用火元素魔法的咒语,而这个咒语就是接口,而再打比方,就是你想要用特效为暴击的魔法攻击,而不同元素实现暴击的方式可以不一样,但表现出来的结果就是暴击,而这个特效为暴击的魔法攻击实际上也就是接口.

  6. 接口是通向多个实现的网关,如果想生成适合某个接口的对象,一种典型的方式是工厂方法(Factory Method)设计模式。你不是直接调用构造器,而是在工厂对象上调用创建方法,它可以产生接口实现。这样,理论上,你的代码与接口实现完全隔离,从而可以透明地将一种实现替换为另一种实现。下面是一个示例,展示了工厂方法的结构:

    // interfaces/Factories.java
    
    interface Service {
    void method1();
    void method2();
    }
    
    interface ServiceFactory {
    Service getService();
    }
    
    class Service1 implements Service {
    Service1() {} // 包访问权限
    @Override public void method1() {
     System.out.println("Service1 method1");
    }
    @Override public void method2() {
     System.out.println("Service1 method2");
    }
    }
    
    class Service1Factory implements ServiceFactory {
    @Override public Service getService() {
     return new Service1();
    }
    }
    
    class Service2 implements Service {
    Service2() {} // 包访问权限
    @Override public void method1() {
     System.out.println("Service2 method1");
    }
    @Override public void method2() {
     System.out.println("Service2 method2");
    }
    }
    
    class Service2Factory implements ServiceFactory {
    @Override public Service getService() {
     return new Service2();
    }
    }
    
    public class Factories {
    public static void
    serviceConsumer(ServiceFactory fact) {
     Service s = fact.getService();
     s.method1();
     s.method2();
    }
    public static void main(String[] args) {
     serviceConsumer(new Service1Factory());
     // 服务是完全可以互换的:
     serviceConsumer(new Service2Factory());
    }
    }
    /* 输出:
    Service1 method1
    Service1 method2
    Service2 method1
    Service2 method2
    */
    

    如果没有工厂方法,你的代码必须在某处指定创建Service的确切类型,以调用相应的构造器。

    为什么要添加这种额外的间接层?一个常见的原因是创建框架。假设你正在创建一个系统来玩游戏,例如想要在同一个棋盘上玩国际象棋和跳棋:

    // interfaces/Games.java
    // 一个使用了工厂方法的游戏框架
    
    interface Game { boolean move(); }
    interface GameFactory { Game getGame(); }
    
    class Checkers implements Game {
    private int moves = 0;
    private static final int MOVES = 3;
    @Override public boolean move() {
     System.out.println("Checkers move " + moves);
     return ++moves != MOVES;
    }
    }
    
    class CheckersFactory implements GameFactory {
    @Override
    public Game getGame() { return new Checkers(); }
    }
    
    class Chess implements Game {
    private int moves = 0;
    private static final int MOVES = 4;
    @Override public boolean move() {
     System.out.println("Chess move " + moves);
     return ++moves != MOVES;
    }
    }
    
    class ChessFactory implements GameFactory {
    @Override
    public Game getGame() { return new Chess(); }
    }
    
    public class Games {
    public static void playGame(GameFactory factory) {
     Game s = factory.getGame();
     while(s.move())
       ;
    }
    public static void main(String[] args) {
     playGame(new CheckersFactory());
     playGame(new ChessFactory());
    }
    }
    /* 输出:
    Checkers move 0
    Checkers move 1
    Checkers move 2
    Chess move 0
    Chess move 1
    Chess move 2
    Chess move 3
    */
    

    如果Games类代表了一段复杂的代码,这种方式意味着你可以在不同类型的游戏中复用该代码。可以想象,更加复杂的游戏可以从这种模式中受益。

    在下一章中,你将看到实现工厂方法的更优雅的方式,那就是使用匿名内部类。

    我看不出来为什么工厂会有这种作用,境界还是太低了

    我自己只能理解到工厂将接口和实现代码完全的分离了,这里的分离指的仅仅只是Game接口和实现Game接口的类,因为这里的Game接口简单,但我用来实现的那个类不一定简单,他可能远比这个复杂得多,而工厂化给出接口类,我自己更便偏向于易读性

  7. sealed类的子类只能通过下面的某个修饰符来定义。

    • final:不允许有进一步的子类。
    • sealed:允许有一组密封子类。
    • non-sealed:一个新关键字,允许未知的子类来继承它。
  8. // interfaces/SealedSubclasses.java
    // {NewFeature}从JDK 17开始
    
    sealed class Bottom permits Level1 {}
    sealed class Level1 extends Bottom permits Level2 {}
    sealed class Level2 extends Level1 permits Level3 {}
    final class Level3 extends Level2 {}
    

    注意,一个sealed必须至少有一个子类。

    这个结构还挺有意思,虽然感觉没啥软用

  9. 一个sealed的基类无法阻止non-sealed的子类的使用,因此可以随时放开限制:

    // interfaces/NonSealed.java
    // {NewFeature} 从JDK 17开始
    
    sealed class Super permits Sub1, Sub2 {}
    final class Sub1 extends Super {}
    non-sealed class Sub2 extends Super {}
    class Any1 extends Sub2 {}
    class Any2 extends Sub2 {}
    

    Sub2允许任意数量的子类,因此它似乎放开了对可以创建的类型的控制。但是,我们还是严格限制了sealedSuper的直接子类。也就是说,Super仍然只能有直接子类Sub1Sub2

    忽然感觉这玩意是专门为了针对向上转型吗?

    package example;
    public class one implements a{
        String s1 = "hello";
        public one (String s1){
            this.s1 = s1;
        }
    
        @Override
        public void cd() {
            ;
        }
    
        public static void main(String[] args){
            a.c();
            a.main(args);
            Super first = new Any1();
        }
    }
    sealed class Super permits Sub1, Sub2 {}
    final class Sub1 extends Super {}
    non-sealed class Sub2 extends Super {}
    class Any1 extends Sub2 {}
    class Any2 extends Sub2 {}
    

    不过我试了下,编译是能过的,看不懂为什么要有这种新特性

    重看了一遍,这个应该是为了画图的时候好画,因为它的修饰符就那么几个,这样就可以使类的层次结构更加鲜明,不过不可否认的是,这样的可拓展性我更感觉还是不太行

  10. // interfaces/PermittedSubclasses.java
    // {NewFeature}从JDK 17开始
    
    sealed class Color permits Red, Green, Blue {}
    final class Red extends Color {}
    final class Green extends Color {}
    final class Blue extends Color {}
    
    public class PermittedSubclasses {
    public static void main(String[] args) {
     for(var p: Color.class.getPermittedSubclasses())
       System.out.println(p.getSimpleName());
    }
    }
    /* 输出:
    Red
    Green
    Blue
    */
    
  11. 我们可能很容易就会觉得接口是好的,因此总是选择接口而不是具体的类。几乎任何要创建一个类的场景,都可以创建一个接口和一个工厂来代替。

    许多人受到了这种诱惑,只要有可能就创建接口和工厂。这里的逻辑似乎是,你可能会用到不同的实现,因此应该始终添加这层抽象。这是一种过早的设计优化。

    任何抽象都应该由真正的需求来驱动。接口应该是在必要时用来重构的东西,而不是在任何地方都多加一个间接层级,进而带来额外的复杂性。这种额外的复杂性影响很大,如果你让某人在克服这种复杂性上花费时间,而他最终却发现你添加接口只不过是为了“以防万一”,而非出于什么令人信服的其他理由——那好吧,如果我看到这样的设计,就会开始质疑这个人做过的其他所有设计。

    一个比较恰当的指导方针是“优先使用类而不是接口”。从类开始设计,如果很明显接口是必要的,那么就重构。接口是一个很好的工具,但它很容易被滥用。

第十一章 内部类

  1. 更普遍的情况是,外部类有一个方法,该方法返回一个指向内部类的引用,正如在to()contents()方法中看到的那样。

    // innerclasses/Parcel2.java
    // 返回一个指向内部类的引用
    
    public class Parcel2 {
    class Contents {
     private int i = 11;
     public int value() { return i; }
    }
    class Destination {
     private String label;
     Destination(String whereTo) {
       label = whereTo;
     }
     String readLabel() { return label; }
    }
    public Destination to(String s) {
     return new Destination(s);
    }
    public Contents contents() {
     return new Contents();
    }
    public void ship(String dest) {
     Contents c = contents();
     Destination d = to(dest);
     System.out.println(d.readLabel());
    }
    public static void main(String[] args) {
     Parcel2 p = new Parcel2();
     p.ship("Tasmania");
     Parcel2 q = new Parcel2();
     // 定义指向内部类的引用:
     Parcel2.Contents c = q.contents();
     Parcel2.Destination d = q.to("Borneo");
    }
    }
    /* 输出:
    Tasmania
    */
    

    要在外部类的非静态方法之外的任何地方创建内部类的对象,必须像在main()中看到的那样,将对象的类型指定为OuterClassName.InnerClassName

    这里如果不在包外的话,可以直接这么写

    public class Parcel2 {
        class Contents {
            private int i = 11;
            public int value() { return i; }
        }
        class Destination {
            private String label;
            Destination(String whereTo) {
                label = whereTo;
            }
            String readLabel() { return label; }
        }
        public Destination to(String s) {
            return new Destination(s);
        }
        public Contents contents() {
            return new Contents();
        }
        public void ship(String dest) {
            Contents c = contents();
            Destination d = to(dest);
            System.out.println(d.readLabel());
        }
        public static void main(String[] args) {
            Parcel2 p = new Parcel2();
            p.ship("Tasmania");
            Parcel2 q = new Parcel2();
            // 定义指向内部类的引用:
            Contents c = q.contents();
            Destination d = q.to("Borneo");
        }
    }
    
  2. // innerclasses/Sequence.java
    // 保存一个对象序列
    
    interface Selector {
    boolean end();
    Object current();
    void next();
    }
    
    public class Sequence {
    private Object[] items;
    private int next = 0;
    public Sequence(int size) {
     items = new Object[size];
    }
    public void add(Object x) {
     if(next < items.length)
       items[next++] = x;
    }
    private class SequenceSelector implements Selector {
     private int i = 0;
     @Override
     public boolean end() { return i == items.length; }
     @Override
     public Object current() { return items[i]; }
     @Override
     public void next() { if(i < items.length) i++; }
    }
    public Selector selector() {
     return new SequenceSelector();
    }
    public static void main(String[] args) {
     Sequence sequence = new Sequence(10);
     for(int i = 0; i < 10; i++)
       sequence.add(Integer.toString(i));
     Selector selector = sequence.selector();
     while(!selector.end()) {
       System.out.print(selector.current() + " ");
       selector.next();
     }
    }
    }
    /* 输出:
    0 1 2 3 4 5 6 7 8 9
    */
    

    Sequence是以类的形式包装起来的定长Object数组。可以调用add()向序列末尾增加一个新的Object(如果还有空间)。要取得 Sequence中的每一个对象,可以使用名为Selector的接口。这是迭代器(Iterator)设计模式的一个例子,我们会在第12章进一步学习。通过Selector,可以检查是否到了Sequence的末尾(end()),访问当前Objectcurrent()),以及移动到下一个Objectnext())。因为Selector是一个接口,所以其他类可以用自己的方式实现该接口,而且其他方法可以以该接口为参数,来创建更通用的代码。

    人看麻了,有点工厂化方法的味道

  3. 内部类的对象在构造时,需要一个指向外围类对象的引用,如果编译器无法访问这个引用,它就会报错。不过这种情况大多不需要程序员干预。

    有一点味道了,因为内部类的对象是从属于他的外部对象的

  4. // innerclasses/DotThis.java
    // 访问外部类对象
    
    public class DotThis {
      void f() { System.out.println("DotThis.f()"); } 
      public class Inner {
        public DotThis outer() {
          return DotThis.this;
          // 如果直接写“this”,引用的会是Inner的“this”
        } 
      }
      public Inner inner() { return new Inner(); } 
      public static void main(String[] args) {
        DotThis dt = new DotThis(); 
        DotThis.Inner dti = dt.inner(); 
        dti.outer().f();
      } 
    }
    /* 输出:
    DotThis.f()
    */
    

    学习如何通过内部类返回外部类

  5. // innerclasses/DotNew.java
    // 使用.new语法直接创建一个内部类的对象
    
    public class DotNew {
    public class Inner {}
    public static void main(String[] args) {
     DotNew dn = new DotNew(); 
     DotNew.Inner dni = dn.new Inner();
    }
    }
    

    要直接创建内部类的对象,你可能会以为,要遵循和前面同样的形式,使用外部类的名字DotNew,然而事实并非如此。我们要使用外部类的对象来创建内部类的对象,正如我们在示例代码中所看到的那样。这也解决了内部类的名字作用域问题,所以我们不用dn.new DotNew.Inner()(确实也不能用)。

    看了半天,才发现我看错了,作者下面解释的那个是用了内部类创建了外部类,再用外部类对应的方法去生成内部类,而这种创建方法本身逻辑就有点不自洽

其他

  1. bit等于一个二进制位
  2. java的switch的使用https://www.w3schools.com/java/java_switch.asp

标签:20,String,19,void,接口,class,new,2022.2,public
来源: https://blog.csdn.net/just_a_fresh_man/article/details/123038098

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有