java编程思想读书笔记二

泛型

  • 对象和实例是一个意思,类与对象的关系就像数据类型和变量一样。

  • 泛型的主要目的之一就是用来指定类(如:容器)要持有什么类型的对象,代码解释如下:

    
    public class Holder3 {
      private T a;//持有了T的对象,此处可以持有任何对象,可以用Object代替但是要向下转型
      public Holder3(T a) { this.a = a; }
      public void set(T a) { this.a = a; }
      public T get() { return a; }
      public static void main(String[] args) {
        Holder3 h3 =
          new Holder3(new Automobile());
        Automobile a = h3.get(); // No cast needed
        // h3.set("Not an Automobile"); // Error
        // h3.set(1); // Error
      }
    } ///:~
    
  • 在有些场景中会有一个方法返回多个对象,你可以使用创建类用它来持有返回的多个对象,如果再 加上泛型技术就会在编译期确保类型安全。代码解释如下:

    //: net/mindview/util/TwoTuple.java
    package net.mindview.util;
    
    public class TwoTuple {
      public final A first;
      public final B second;
      public TwoTuple(A a, B b) { first = a; second = b; }
      public String toString() {
        return "(" + first + ", " + second + ")";
      }
    } ///:~
    
  • 如果泛型用得好,基本上不用强制性转换

  • 泛型也可以应用于接口,比如public interface Generator,在写继承的时候T可以写成任意类型,比如构造一个咖啡工厂public class CoffeeGenerator implements Generator,构造一个生成Fibonacci数列的类public class Fibonacci implements Generator,咖啡代码如下:

    //... 省略处为一些简单类,如Latte,Mocha,Cappuccino,Americano,Breve这些类都继承于coffee,coffee.java如下
    package com.generics.coffee;
    public class Coffee {
      private static long counter = 0;
      private final long id = counter++;
      public String toString() {
        return getClass().getSimpleName() + " " + id;
      }
    } ///:~
    
    //: generics/coffee/CoffeeGenerator.java
    // Generate different types of Coffee:
    package com.generics.coffee;
    import java.util.*;
    import net.mindview.util.*;
    
    public class CoffeeGenerator implements Generator ,Iterable {
      @SuppressWarnings("rawtypes")
      private Class[] types = { Latte.class, Mocha.class,
        Cappuccino.class, Americano.class, Breve.class, };
      private static Random rand = new Random(47);
      public CoffeeGenerator() {}
      // For iteration:
      private int size = 0;
      public CoffeeGenerator(int sz) { size = sz; } 
      public Coffee next() {
        try {
          return (Coffee)
            types[rand.nextInt(types.length)].newInstance();
          // Report programmer errors at run time:
        } catch(Exception e) {
          throw new RuntimeException(e);
        }
      }
      //解释:内部类实现迭代器,该实现了Iterator而CoffeeGenerator实现的是Iterable,要实现foreach循环必须实现这两个接口,
    //从代码看起来foreach循环是看出来了,要理解其本质的原理需要看jvm里面的字节码,new CoffeeGenerator(5)调用后,首先产生
    //CoffeeIterator的实例,执行hasNext()->next()
    //此处可以也可以用匿名内部类
      class CoffeeIterator implements Iterator {
        int count = size;
        public boolean hasNext() { return count > 0; }
        public Coffee next() {
          count--;
          return CoffeeGenerator.this.next();
        }
        public void remove() { // Not implemented
          throw new UnsupportedOperationException();
        }
      };    
      public Iterator iterator() {
        return new CoffeeIterator();
      }
      public static void main(String[] args) {
        CoffeeGenerator gen = new CoffeeGenerator();
        for(int i = 0; i < 5; i++)
          System.out.println(gen.next());
        for(Coffee c : new CoffeeGenerator(5))
          System.out.println(c);
      }
    } /* Output:
    Americano 0
    Latte 1
    Americano 2
    Mocha 3
    Mocha 4
    Breve 5
    Americano 6
    Latte 7
    Cappuccino 8
    Cappuccino 9
    *///:~
    
    Fibonacci数列的代码如下:
    
    package com.generics;
      import net.mindview.util.*;
    
      public class Fibonacci implements Generator {
        private int count = 0;
        public Integer next() { return fib(count++); }
        private int fib(int n) {
          if(n < 2) return 1;
          return fib(n-2) + fib(n-1);
        }
        public static void main(String[] args) {
          Fibonacci gen = new Fibonacci();
          for(int i = 0; i < 18; i++)
            System.out.print(gen.next() + " ");
        }
      } /* Output:
      1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
      *///:~
    

    如果想要实现迭代,而且要不用内部类的方式(CoffeeGenerator.java使用的是内部类实现的迭代器模式),用适配器模式实现,适配器模式即把两个互不相关的接口或者类相连接,所以可以使用继承或者组合,UML如下:

    ![](https://www.processon.com/chart_image/thumb/58aaf16ee4b071585833fd96.png)
    
    迭代如下:
    
    package com.generics;
      // Adapt the Fibonacci class to make it Iterable.
      import java.util.*;
      //组合来创建适配器
      public class IterableFibonacci implements Iterable {
        private Fibonacci fibonacci = new Fibonacci();
        private int n;
        public IterableFibonacci(int count) { n = count; }
        public Iterator iterator() {
        //匿名内部类的形式
        return new Iterator() {
        @Override
        public Integer next() {
            // TODO Auto-generated method stub
            n--;
        
            return fibonacci.next();//invoke next() in Fibonacci,for this extends Fibonacci
        }
        @Override
        public boolean hasNext() {
            // TODO Auto-generated method stub
            return n > 0; 
        }
        public void remove() { // Not implemented
                throw new UnsupportedOperationException();
              }
        };
        }   
        public static void main(String[] args) {
          for(int i : new IterableFibonacci(18))
            System.out.print(i + " ");
        }
      } /* Output:
      1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
      *///:~ 
    
  • 泛型应用于方法

    • 应用于方法public void f(T x){},其中一定要写,不然编译器是无法识别出参数的T

    • 当可变参数与方法结合:

      package com.generics;
      
      //: generics/GenericVarargs.java
      import java.util.*;
      
      public class GenericVarargs {
        //此处的makeList就像是java.util.Arrays里面的asList(T... args);
        public static  List makeList(T... args) {
          List result = new ArrayList();
          for(T item : args)
            result.add(item);
          return result;
        }
        public static void main(String[] args) {
          List ls = makeList("A");
          System.out.println(ls);
          ls = makeList("A", "B", "C");
          System.out.println(ls);
          ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
          System.out.println(ls);
        }
      } /* Output:
                                                  [A]
                                                  [A, B, C]
                                                  [, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
                                                  *///:~
      

      从上面代码注释可以看到makeList和asList方法很像,下面来看看asList的源码分析

        public static  List asList(T... a) {
            return new ArrayList<>(a);//此处的ArrayList不是你想的java.util.ArrayList,他是Arrays里面的一个静态内部类
          }
          //此处是静态内部类的构造器,返回一个数组,需要说明的是该内部类并没有实现add,remove等方法,因为asList()方法在大多数使用场景中是不用改变的,所以要构造一个可编辑的ArrayList()用类似下面的代码即可List levelList = new ArrayList(Arrays.asList("a", "b", "c"));  
          ArrayList(E[] array) {
            a = Objects.requireNonNull(array);//判断array是否为空
          }
      
  • 利用泛型方法对前一章的生成器进行更一步的抽象,代码如下:

    //: net/mindview/util/BasicGenerator.java
    // Automatically create a Generator, given a class
    // with a default (no-arg) constructor.
    package net.mindview.util;
    //this class can generate any Class which have default constructor by create() function,but there is a limit which is that constructor cannot pass argument(传参) 
    public class BasicGenerator implements Generator {
      private Class type; 
      public BasicGenerator(Class type){ this.type = type; }
      public T next() {
        try {
          // Assumes type is a public class:
          return type.newInstance();
        } catch(Exception e) {
          throw new RuntimeException(e);
        }
      }
      // Produce a Default generator given a type token:
      public static  Generator create(Class type) {
        return new BasicGenerator(type);
      }
    } ///:~
    

    更多的,我们可以对前面提到的元组进行进一步的抽象

    //: net/mindview/util/Tuple.java
    // Tuple library using type argument inference.
    package net.mindview.util;
    
    public class Tuple {
      public static  TwoTuple tuple(A a, B b) {
        return new TwoTuple(a, b);
      }
      public static  ThreeTuple
      tuple(A a, B b, C c) {
        return new ThreeTuple(a, b, c);
      }
      public static  FourTuple
      tuple(A a, B b, C c, D d) {
        return new FourTuple(a, b, c, d);
      }
      public static 
      FiveTuple tuple(A a, B b, C c, D d, E e) {
        return new FiveTuple(a, b, c, d, e);
      }
    } ///:~
    
  • java对泛型的擦除有四句话

    • 泛型类型在运行时都是Object类型
    • 模板只在编译阶段有效是为了提供编译期的类型安全,通过反射操作可以绕过编译阶段
    • 在编译期就可以知道的类型信息是可以操作的
    • 所有在运行时才能知道类型信息的操作都将无法工作
    package com.generics;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    class Manipulator {
      public T obj;
      public Manipulator(T x) { obj = x; }
      // Error: cannot find symbol: method f():
      public void manipulate() { 
    //    (obj).f();
      }
      
      public void erase2(){
          //T[] t = new T[4]; 
          //a instanceof T;
      }
    }
    
    public class Manipulation {
      public static void main(String[] args) {
        //1.模板只在编译阶段有效是为了提供编译期的类型安全,通过反射操作可以绕过编译阶段
        List list1 = new ArrayList<>();
        List list = new ArrayList<>();
        list1.add("s");
        //list1.add(2);
        try {
            Method m = ArrayList.class.getMethod("add",Object.class);
            m.invoke(list1, 2);
            System.out.println(list1);
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //2.在编译期就可以知道的类型信息是可以操作的
        HasF hf = new HasF();
        Manipulator manipulator =
          new Manipulator(hf);
        manipulator.obj.f();
       //在这个函数里面调用的obj.f(),是不可能考虑客户端的类型,即是单独去编译的,在客户端没有调用时,他并不知道T是什么类型,所以有错
        manipulator.manipulate();
      
        //3.所有在运行时才能知道类型信息的操作都将无法工作
        manipulator.erase2();
        
      }
    } ///:~
    
  • 不能创建泛型类型数组的,一般的解决方案是在任何想要创建泛型数组的地方都是用ArrayList去创建。

  • 泛型的主要目标之一是将错误检测移入到编译期

  • 编译器直接拒绝对参数列表中涉及到的通配符的方法,即add(? extends fruit)如果变成了这样结果如下图

    java编程思想读书笔记二_第1张图片
  • 下面代码能够实现只能存水果的集合,且在编译期内就能检查类型信息,其中第二种方式称之为逆变。为什么逆变的方式可以实现?答:super关键字表示下界,List fruit = new ArrayList<>();,而?必须要表示一个确切的类型,准确来讲应该是这样声明一个实例即:List fruit = new ArrayList<在这个括号内部必须是Apple的父类>();即在比如List fruit = new ArrayList(),所以当add()的时候,可以插入Apple的子类,同样的道理分析List flist2 = new ArrayList<这里面要插入的是Apple的子类>();所以当add(new Apple())时候,会失败,比如List flist2 = new ArrayList();Jonathan = new Apple()//error;

    //1.想要实现一个集合里面能装所有类型的水果,但是在编译期不允许装除了水果以外的其他对象
    List flist3 = new ArrayList<>();
    flist3.addAll(Arrays.asList(new Apple()));
    flist3.addAll(Arrays.asList(new Orange()));
    System.out.println(flist3.get(1));
    //2.第一种方式太复杂,下面用逆变的方式实现
    List fruit = new ArrayList<>();
    fruit.add(new Apple());
    fruit.add(new Orange());
    
  • 混型即Timestamped> mixin其中mixin能够调用基类的所有函数,在C++中,这是显然的,但是在java中可以这样声明,但不能调用基类的任何函数只能调用Timestamped类中的函数,所以必须使用有些设计模式来代替,其中涉及到装饰器模式,和用动态代理(即我们可以动态注入类方法)来实现混合,但是结果都没有C++中方便直接。

    implementsextends关键字实现:

    package com.generics;
    //: generics/Mixins.java
    import java.util.*;
    
    interface TimeStamped { long getStamp(); }
    
    class TimeStampedImp implements TimeStamped {
      private final long timeStamp;
      public TimeStampedImp() {
        timeStamp = new Date().getTime();
      }
      public long getStamp() { return timeStamp; }
    }
    
    interface SerialNumbered { long getSerialNumber(); }
    
    class SerialNumberedImp implements SerialNumbered {
      private static long counter = 1;
      private final long serialNumber = counter++;
      public long getSerialNumber() { return serialNumber; }
    }
    
    interface Basic {
      public void set(String val);
      public String get();
    }
    
    class BasicImp implements Basic {
      private String value;
      public void set(String val) { value = val; }
      public String get() { return value; }
    }
    // for Mixin2.java,Timestamped> mixin = new Timestamped();,mixin can not invoke set() function of Basic,but c++ is capable to do it.
    //so in java, use implements and extends keywords to realize it.
    class Mixin extends BasicImp
    implements TimeStamped, SerialNumbered {
    
     //if use this,you must have a instance of response to interface
    
      private TimeStamped timeStamp = new TimeStampedImp();
      private SerialNumbered serialNumber =
        new SerialNumberedImp();
      public long getStamp() { return timeStamp.getStamp(); }
      public long getSerialNumber() {
        return serialNumber.getSerialNumber();
      }
    }
    
    public class Mixins {
      public static void main(String[] args) {
        Mixin mixin1 = new Mixin(), mixin2 = new Mixin();
        mixin1.set("test string 1");
        mixin2.set("test string 2");
        System.out.println(mixin1.get() + " " +
          mixin1.getStamp() +  " " + mixin1.getSerialNumber());
        System.out.println(mixin2.get() + " " +
          mixin2.getStamp() +  " " + mixin2.getSerialNumber());
      }
    } /* Output: (Sample)
    test string 1 1132437151359 1
    test string 2 1132437151359 2
    *///:~
    

    装饰器模式实现(并没有完全实现):

    package com.generics.decorator;
    
    //: generics/decorator/Decoration.java
    
    import java.util.*;
    
    class Basic {
      private String value;
      public void set(String val) { value = val; }
      public String get() { return value; }
    }
    
    class Decorator extends Basic {
      protected Basic basic;
      public Decorator(Basic basic) { this.basic = basic; }
      public void set(String val) { basic.set(val); }
      public String get() { return basic.get(); }
    }   
    
    class TimeStamped extends Decorator {
      private final long timeStamp;
      public TimeStamped(Basic basic) {
        super(basic);
        timeStamp = new Date().getTime();
      }
      public long getStamp() { return timeStamp; }
    }
    
    class SerialNumbered extends Decorator {
      private static long counter = 1;
      private final long serialNumber = counter++;
      public SerialNumbered(Basic basic) { super(basic); }
      public long getSerialNumber() { return serialNumber; }
    }   
    
    //this is decoration design patterns
    
    public class Decoration {
      public static void main(String[] args) {
        TimeStamped t = new TimeStamped(new Basic());
      // because timestamped extends Basic
     t.set("fasdfa");
      //realize such as TimeStamped> mixin1, mixin2
        TimeStamped t2 = new TimeStamped(
          new SerialNumbered(new Basic()));
        //! t2.getSerialNumber(); // Not available, obviously
        SerialNumbered s = new SerialNumbered(new Basic());
        SerialNumbered s2 = new SerialNumbered(
          new TimeStamped(new Basic()));
        //! s2.getStamp(); // Not available
      }
    } ///:~
    

    动态代理模式实现:

    package com.generics;
    
    //: generics/DynamicProxyMixin.java
    import java.lang.reflect.*;
    import java.util.*;
    import net.mindview.util.*;
    import static net.mindview.util.Tuple.*;
    
    class MixinProxy implements InvocationHandler {
      Map delegatesByMethod;
      public MixinProxy(TwoTuple>... pairs) {
        delegatesByMethod = new HashMap();
        for(TwoTuple> pair : pairs) {
          for(Method method : pair.second.getMethods()) {
            String methodName = method.getName();
            System.out.println(methodName + "()");
            // The first interface in the map
            // implements the method.
            if (!delegatesByMethod.containsKey(methodName))
              delegatesByMethod.put(methodName, pair.first);// this is the most important, because this inject all functions of pairs
          }
        }
      } 
      public Object invoke(Object proxy, Method method,
        Object[] args) throws Throwable {
        System.out.println("invoke() is invoked"); 
        String methodName = method.getName();
        Object delegate = delegatesByMethod.get(methodName);
        return method.invoke(delegate, args);
      }
      @SuppressWarnings("unchecked")
      public static Object newInstance(TwoTuple... pairs) {
        Class[] interfaces = new Class[pairs.length];
        for(int i = 0; i < pairs.length; i++) {
          interfaces[i] = (Class)pairs[i].second;//second represent XXX.class
        }
        ClassLoader cl =
          pairs[0].first.getClass().getClassLoader();
        return Proxy.newProxyInstance(
          cl, interfaces, new MixinProxy(pairs));
      }
    }   
    
    public class DynamicProxyMixin {
      public static void main(String[] args) {
        Object mixin = MixinProxy.newInstance(
          tuple(new BasicImp(), Basic.class),
          tuple(new TimeStampedImp(), TimeStamped.class),
          tuple(new SerialNumberedImp(),SerialNumbered.class));
        //
        Basic b = (Basic)mixin;
        TimeStamped t = (TimeStamped)mixin;
        SerialNumbered s = (SerialNumbered)mixin;
        b.set("Hello");
        System.out.println(b.get());
        System.out.println(t.getStamp());
        System.out.println(s.getSerialNumber());
      }
    } /* get()
    set()
    getStamp()
    getSerialNumber()
    invoke() is invoked
    invoke() is invoked
    Hello
    invoke() is invoked
    1489219456567
    invoke() is invoked
    1
    *///:~
    
  • 静态类型检查即在程序没有运行时就能够通过检查源代码确定类型安全,与动态类型相对应

  • 潜在类型机制即直接可以用模板T,而不用指定该模板属于哪个基类,比如在C++里面就可以直接定义

    template void perform(T anything) {
      anything.speak();
      anything.sit();
    }
    

    而在java中必须要指明边界

    class Communicate {
    //must specify the bounds of generic type,but C++ is not necessary
    public static  void perform(T performer) {
    performer.speak();
    performer.sit();
    }
    }
    
  • java对潜在类型机制的补偿的一种方式是反射,如下

    class CommunicateReflectively {
    //接受一个Object,然后看是哪个类
    public static void perform(Object speaker) {
    Class spkr = speaker.getClass();
    try {
    try {
    Method speak = spkr.getMethod("speak");
    speak.invoke(speaker);
    } catch(NoSuchMethodException e) {
    print(speaker + " cannot speak");
    }
    try {
    Method sit = spkr.getMethod("sit");
    sit.invoke(speaker);
    } catch(NoSuchMethodException e) {
    print(speaker + " cannot sit");
    }
    } catch(Exception e) {
    throw new RuntimeException(speaker.toString(), e);
    }
    }
    }
    
  • 15.17中15.17.2与15.17.3,15.17.4没理解

  • 应用于序列的泛型技术很多都会涉及到Iterable接口

数组

  • 在java中数组是一种效率最高的存储和随机访问对象应用序列的方式

  • Comparable接口和Comaprator接口用于排序,jdk中运用策略设计模式将“保持不变的事物与会发生改变的事物相分离”,代码如下:

    //Comparable
    class Student implements Comparable{
        private String name;
        private int age;
        private float score;
        
        public Student(String name, int age, float score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
        
        public String toString()
        {
            return name+"\t\t"+age+"\t\t"+score;
        }
    
        @Override
        public int compareTo(Student o) {
            // TODO Auto-generated method stub
            if(this.score>o.score)//score是private的,为什么能够直接调用,这是因为在Student类内部
                return -1;//由高到底排序
            else if(this.scoreo.age)
                    return 1;//由底到高排序
                else if(this.age{
    
        @Override
        public int compare(Student o1, Student o2) {
            // TODO Auto-generated method stub
            if(o1.getScore()>o2.getScore())
                return -1;
            else if(o1.getScore()o2.getAge())
                    return 1;
                else if(o1.getAge()
  • 当你使用最近的java版本编程时,应该优先选择容器而不是数组,只有在证明性能成为问题时,你才应该讲程序重构为使用数组

容器源码解读

  • 继承结构代码如下:
      public interface Iterable{...}
    
    public interface Collection extends Iterable{...}
    public interface Set extends Collection{...}
    public interface SortedSet extends Set{
    Comparator comparator();
    
    }
    public interface List extends Collection{...}
    
  • 抽象类实现接口,可以不用实现其全部的方法即可以筛选一些方法来实现,比如:
interface test{
    void m();
    void f();
  }
  abstract class test2 implements test{
    @Override
    public void m() {
        // TODO Auto-generated method stub
    }
  }

你可能感兴趣的:(java编程思想读书笔记二)