java编程思想读书笔记二

泛型

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

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

    
    public class Holder3<T> {
      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<Automobile> h3 =
          new Holder3<Automobile>(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<A,B> {
      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>,在写继承的时候T可以写成任意类型,比如构造一个咖啡工厂public class CoffeeGenerator implements Generator<Coffee>,构造一个生成Fibonacci数列的类public class Fibonacci implements Generator<Integer>,咖啡代码如下:

    //... 省略处为一些简单类,如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<Coffee> ,Iterable<Coffee> {
      @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<Coffee> {
        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<Coffee> 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<Integer> {
        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<Integer> {
        private Fibonacci fibonacci = new Fibonacci();
        private int n;
        public IterableFibonacci(int count) { n = count; }
        public Iterator<Integer> iterator() {
        //匿名内部类的形式
        return new Iterator<Integer>() {
        @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 <T> void f(T x){},其中<T>一定要写,不然编译器是无法识别出参数的T

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

      package com.generics;
      
      //: generics/GenericVarargs.java
      import java.util.*;
      
      public class GenericVarargs {
        //此处的makeList就像是java.util.Arrays里面的asList(T... args);
        public static <T> List<T> makeList(T... args) {
          List<T> result = new ArrayList<T>();
          for(T item : args)
            result.add(item);
          return result;
        }
        public static void main(String[] args) {
          List<String> 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 <T> List<T> asList(T... a) {
            return new ArrayList<>(a);//此处的ArrayList不是你想的java.util.ArrayList,他是Arrays里面的一个静态内部类
          }
          //此处是静态内部类的构造器,返回一个数组,需要说明的是该内部类并没有实现add,remove等方法,因为asList()方法在大多数使用场景中是不用改变的,所以要构造一个可编辑的ArrayList()用类似下面的代码即可List<WaiterLevel> levelList = new ArrayList<WaiterLevel>(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<T> implements Generator<T> {
      private Class<T> type; 
      public BasicGenerator(Class<T> 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 <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<T>(type);
      }
    } ///:~
    

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

    //: net/mindview/util/Tuple.java
    // Tuple library using type argument inference.
    package net.mindview.util;
    
    public class Tuple {
      public static <A,B> TwoTuple<A,B> tuple(A a, B b) {
        return new TwoTuple<A,B>(a, b);
      }
      public static <A,B,C> ThreeTuple<A,B,C>
      tuple(A a, B b, C c) {
        return new ThreeTuple<A,B,C>(a, b, c);
      }
      public static <A,B,C,D> FourTuple<A,B,C,D>
      tuple(A a, B b, C c, D d) {
        return new FourTuple<A,B,C,D>(a, b, c, d);
      }
      public static <A,B,C,D,E>
      FiveTuple<A,B,C,D,E> tuple(A a, B b, C c, D d, E e) {
        return new FiveTuple<A,B,C,D,E>(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<T> {
      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<String> 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<HasF> manipulator =
          new Manipulator<HasF>(hf);
        manipulator.obj.f();
       //在这个函数里面调用的obj.f(),是不可能考虑客户端的类型,即是单独去编译的,在客户端没有调用时,他并不知道T是什么类型,所以有错
        manipulator.manipulate();
      
        //3.所有在运行时才能知道类型信息的操作都将无法工作
        manipulator.erase2();
        
      }
    } ///:~
    
  • 不能创建泛型类型数组的,一般的解决方案是在任何想要创建泛型数组的地方都是用ArrayList去创建。

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

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

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

    //1.想要实现一个集合里面能装所有类型的水果,但是在编译期不允许装除了水果以外的其他对象
    List<Fruit> flist3 = new ArrayList<>();
    flist3.addAll(Arrays.asList(new Apple()));
    flist3.addAll(Arrays.asList(new Orange()));
    System.out.println(flist3.get(1));
    //2.第一种方式太复杂,下面用逆变的方式实现
    List<? super Fruit> fruit = new ArrayList<>();
    fruit.add(new Apple());
    fruit.add(new Orange());
    
  • 混型即Timestamped<Serialnumbered<Basic>> 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<Serialnumbered<Basic>> 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<SerialNumbered<Basic>> 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<String,Object> delegatesByMethod;
      public MixinProxy(TwoTuple<Object,Class<?>>... pairs) {
        delegatesByMethod = new HashMap<String,Object>();
        for(TwoTuple<Object,Class<?>> 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<class T> 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 <T extends Performs> 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<T>接口

数组

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

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

    //Comparable
    class Student implements Comparable<Student>{
        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.score<o.score)
                return 1;
            else{
                if(this.age>o.age)
                    return 1;//由底到高排序
                else if(this.age<o.age)
                    return -1;
                else
                    return 0;
            }
        }
    }
    
    public class ComparableDemo01 {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Student stu[]={new Student("zhangsan",20,90.0f),
                    new Student("lisi",22,90.0f),
                    new Student("wangwu",20,99.0f),
                    new Student("sunliu",22,100.0f)};
            java.util.Arrays.sort(stu);
            for(Student s:stu)
            {
                System.out.println(s);
            }
        }
    }
    
    //Comparator
    package edu.sjtu.ist.comutil;
    
    import java.util.Comparator;
    
    class Student {
        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 getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public float getScore() {
            return score;
        }
        public void setScore(float score) {
            this.score = score;
        }
    
        public String toString()
        {
            return name+"\t\t"+age+"\t\t"+score;
        }
    
    }
    
    class StudentComparator implements Comparator<Student>{
    
        @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.getScore())
                return 1;
            else{
                if(o1.getAge()>o2.getAge())
                    return 1;
                else if(o1.getAge()<o2.getAge())
                    return -1;
                else 
                    return 0;
            }
        }
        
    }
    public class ComparableDemo02 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            Student stu[]={new Student("zhangsan",20,90.0f),
                    new Student("lisi",22,90.0f),
                    new Student("wangwu",20,99.0f),
                    new Student("sunliu",22,100.0f)};
            java.util.Arrays.sort(stu,new StudentComparator());
            for(Student s:stu)
            {
                System.out.println(s);
            }
        }
    
    }
    
  • 当你使用最近的java版本编程时,应该优先选择容器而不是数组,只有在证明性能成为问题时,你才应该讲程序重构为使用数组

容器源码解读

  • 继承结构代码如下:
      public interface Iterable<T>{...}
    
    public interface Collection<E> extends Iterable<E>{...}
    public interface Set<E> extends Collection<E>{...}
    public interface SortedSet<E> extends Set<E>{
    Comparator<? super E> comparator();
    
    }
    public interface List<E> extends Collection<E>{...}
    
  • 抽象类实现接口,可以不用实现其全部的方法即可以筛选一些方法来实现,比如:
interface test{
    void m();
    void f();
  }
  abstract class test2 implements test{
    @Override
    public void m() {
        // TODO Auto-generated method stub
    }
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,694评论 0 3
  • 50道经典Java编程练习题,将数学思维运用到编程中来。抱歉哈找不到文章的原贴了,有冒犯的麻烦知会声哈~ 1.指数...
    OSET我要编程阅读 6,950评论 0 9
  • Java经典问题算法大全 /*【程序1】 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子...
    赵宇_阿特奇阅读 1,850评论 0 2
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,578评论 1 114
  • /*【程序21】 * 作者 南枫题目:求1+2!+3!+...+20!的和 1. 程序分析:此程序只是把累加变成了...
    HUC南枫阅读 431评论 0 0