Java学习笔记6

UDP socket

这种信息传输方式相当于传真,信息打包,在接受端准备纸。

先由客户端给服务器发消息。以告诉服务器客户端的地址。

特点:

1) 基于UDP无连接协议

2) 不保证消息的可靠传输

3) 它们由Java技术中的DatagramSocketDatagramPacket类支持

DatagramSocket(邮递员):对应数据报的Socket概念,不需要创建两个socket,不可使用输入输出流。

DatagramPacket(信件):数据包,是UDP下进行传输数据的单位,数据存放在字节数组中,其中包括了目标地址和端口以及传送的信息(所以不用建立点对点的连接)。

DatagramPacket的分类:

用于接收:DatagramPacket(byte[] buf,int length)

              DatagramPacket(byte[] buf,int offset,int length)

用于发送:DatagramPacket(byte[] buf,int length, InetAddress address,int port )

              DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)

注:InetAddress类网址用于封装IP地址

没有构造方法,通过

InetAddress.getByAddress(byte[] addr):InetAddress

InetAddress.getByName(String host):InetAddress

等。

 

建立UDP 发送端

 

创建一个UDP的客户端的程序的步骤:

 

    1). 创建一个DatagramPacket,其中包含发送的数据和接收方的IP地址和端口

号。

    2). 创建一个DatagramSocket,其中包含了发送方的IP地址和端口号。

    3). 发送数据

    4). 关闭DatagramSocket

byte[] buf = new byte[1024];

DatagramSocket datagramSocket = new DatagramSocket(13);// set port

DatagramPackage intputPackage = new DatagramPackage(buf,buf.length);

datagramSocket.receive(inputPackage);

DatagramPackage outputPackage = new DatagramPackage(buf,0buf.length,inetAddress,port);

datagramSocket.send(outputPackage); //客户端向服务器发信的过程

没建立流所以不用断开。

 

建立UDP 接受端

 

创建一个UDP的服务器端的程序的步骤:

 

    1). 创建一个DatagramPacket,用于存储发送方发送的数据及发送方的IP地址和端口号。

    2). 创建一个DatagramSocket,其中指定了接收方的IP地址和端口号。

    3). 接收数据

    4). 关闭DatagramSocket

byte[] buf = new byte[1024];

DatagramSocket datagramSocket = new DatagramSocket();//不用设端口,因为发送的包中端口

DatagramPackage outputPackage=new DatagramPackage(Buf,buf.length,serverAddress,serverPort);

DatagramPackage inputPackage=new DatagramPackage(buf,0buf.length);  //收信之前要准备一封空信

datagramSocket.receive(inputPackage);

 

UDP处理多客户端,不必多线程,回信可以一封一封的回

在收发信设置为死循环

 

java.net.URL 统一资源定位器

定位在internet上的某一资源

更高层的网络传输

常用格式

协议名://主机名:端口号/定位寻找主机内部的某一资源

http://www.tarena.com.cn:80/bin/index.html

 

 

作用:

1、必须完成

网络聊天室,以广播的形式传输数据

2、QQ聊天室

实现点对点的数据传输

3、统计一个目录下的所有“.java”文件及子目录下的".java"文件总共多少行

 

                                       CoreJava第十七天         2007-5-21

                       

一、Java5.0新特性                       

1、编译器的功能更加强大,JVM变化不大

2Java在逐渐与C++融合

3、五小点,四大点

 

二、五小点

1、自动封箱AutoBoxing/自动解封

   自动封箱和自动拆箱,它实现了简单类型和封装类型的相互转化时,实现了自动转化。 

    byte b     -128~127

    Byte b     在以上数量的基础上多一个null

 

   简单类型和封装类型之间的差别

   封装类可以等于null  ,避免数字得时的二义性。

   Integer i=null

   int ii=i  //会抛出NullException 异常。相当于 int ii=i.intValue();

   Integer i=1 //相当于Integer i=new Integer(1);

   i++;    // i = new Integer(i.intValue()+1);

 

   在基本数据类型和封装类之间的自动转换

   5.0之前

   Integer i=new Integer4);

   int  ii= i.intValue();

 

   5.0之后

   Integer i=4

   Long l=4.3;

  

   public void m(int i){......}

   public void m(Integer i){......}

   以上两个函数也叫方法重载

   自动封箱解箱只在必要的时候才进行。能不封箱找到匹配的就不封箱。

  

2、静态引入 StaticImport

   使用类中静态方法时不用写类名

  

   System.out.println(Math.round(PI));

   可以用以下代码实现:

   import static java.lang.System.*;  //注意,要写类名.* "

   import static java.lang.Math.*;

   out.println(round(PI));

  

   注意:静态引入的方法不能重名

  

3for-each

   统一了遍历数组和遍历集合的方式  

   for(Object o:list){     //Object o 表示每个元素的类型 list 表示要遍历的数组或集合的名字

       System.out.println(o);  //打印集合或数组中的每个元素

   }

  

4、可变长参数

   处理方法重载中,参数类型相同,个数不同的情况     

   public void m(int... is){.....}

  

   int... is 相当于一个 int[] is

   编译器会把给定的参数封装到一个数组中,再传给方法

   在一个方法中只能有一个可变长参数,而且,必须放在最后一个参数的位置

  

5、格式化输入/输出

   java.util.Formatter 对格式的描述

   System.out.printf("Hello %s",str);  //打印字符串类型的变量,用一个占位符  

  

   格式化I/O(Formatted I/O)

   java.util.Sacner类可以进行格式化的输入,可以使用控制台输入,结合了BufferedReaderStringTokener的功能。

  

三、四大点  

1、枚举

  枚举是一个类,并且这个类的对象是现成的,在定义类的时候,即定义好了对象

  程序员要使用的时候,只能从中选择,无权创建

  enum 枚举名{

         枚举值1(..),枚举值2(..).....;

  }

 

1 5.0之前使用模式做出一个面向对象的枚举

    final class Season{

         public static final Season SPRING=new Season();

         public static final Season WINTER=new Season();

         public static final Season SUMMER=new Season();

        public static final Season AUTUMN=new Season();

        private Season(){}

    }

    完全等价于

    enum Season2{

         SPRING(..),//枚举值

         SUMMER(..),

         AUTUMN(..),

         WINTER(..)

    }     

 

    枚举本质上也是一个类,Enum是枚举的父类。

    这个类编译以后生成一个.class

    这个类有构造方法,但是是私有的

 

    枚举中的values()方法会返回枚举中的所有枚举值

    枚举中可以定义方法和属性,最后的一个枚举值要以分号和类定义分开,枚举中可以定义的构造方法。

    枚举不能继承类(本身有父类),但可以实现接口,枚举不能有子类也就是final的,枚举的构造方法是private(私有的)。

    枚举中可以定义抽象方法,可以在枚举值的值中实现抽象方法。

    枚举值就是枚举的对象,枚举默认是final,枚举值可以隐含的匿名内部类来实现枚举中定义抽象方法。

 

2)枚举类(Enumeration Classes)和类一样,具有类所有特性。Season2的父类是java.lang.Enum;

    隐含方法: 每个枚举类型都有的方法。

    Season2[] ss=Season2.values();     ----获得所有的枚举值 

    for(Season2 s:ss){

      System.out.println(s.name());   ----- 打印枚举值

      System.out.println(s.ordinal()); ----- 打印枚举值的编号

    }

 

 

3  enum可以switch中使用(不加类名)。

  switch( s ){

         case SPRING:

                            …………….

         case SUMMER:

                            …………….

         …………..

  }

 

 

4)枚举的有参构造

  enum Season2{

         SPRING(“春”),-------------------------------逗号

         SUMMER(“夏”),-------------------------------逗号

         AUTUMN(“秋”),-------------------------------逗号

         WINTER(“冬”);-------------------------------分号

         private String name;

         Season2(String name){    //构造方法必须是私有的,可以不写private,默认就是私有的

                   this.name=name;

        }

        String getName(){

                 return name;

        }

 

  }

  Season2.SPRING.getName()         ---------------------

 

 

5)枚举中定义的抽象方法,由枚举值实现:

  enum Operation{

         ADD('+'){

                   public double calculate(double s1,double s2){

                            return s1+s2;

                   }

         },

         SUBSTRACT('-'){

                   public double calculate(double s1,double s2){

                            return s1-s2;

                   }

         },

         MULTIPLY('*'){

                   public double calculate(double s1,double s2){

                            return s1*s2;

                   }

         }

         DIVIDE('/'){

                   public double calculate(double s1,double s2){

                            return s1/s2;

                   }

         }

         char name;

         public char getName(){

                   return this.name;

         }

         Operation(char name){

                   this.name=name;

         }

         public abstract double calculate(double s1 ,double s2);

  }

  有抽象方法枚举元素必须实现该方法。

 

  Operator[] os = Operator.values();

  for(Operator o:os){

     System.out.println("8 "+o.name()+" 2="+o.calculate(8,2));

  }

  for(Operator o:os){

     System.out.println("8 "+o.getName()+" 2="+o.calculate(8,2));

  } 

 

  运行结果:

  8 ADD 2=10.0

  8 SUBSTRACT 2=6.0

  8 MULTIPLY 2=16.0

  8 DIVIDE 2=4.0

  8 + 2=10.0

  8 - 2=6.0

  8 * 2=16.0

  8 / 2=4.0

 

 

2、泛型 

(1)增强了java的类型安全,可以在编译期间对容器内的对象进行类型检查,在运行期不必进行类型的转换。

   而在java se5.0之前必须在运行期动态进行容器内对象的检查及转换,泛型是编译时概念,运行时没有泛型

 

   减少含糊的容器,可以定义什么类型的数据放入容器

  

(2)List<Integer> aList = new ArrayList<Integer>();

   aList.add(new Integer(1));

   // ...

   Integer myInteger = aList.get(0);  //从集合中得到的元素不必强制类型转换

   支持泛型的集合,只能存放制定的类型,或者是指定类型的子类型。  

 

   HashMap<String,Float> hm = new HashMap<String,Float>();

  

   不能使用原始类型

   GenList<int> nList = new GenList<int>(); //编译错误

  

   编译类型的泛型和运行时类型的泛型一定要一致。没有多态。

   List<Dog> as = new ArrayList<Dog>();

   List<Animal> l = as;  //error  AnimalDog的父子关系不能推导出List<Animal>  List<Dog> 之间的父子类关系

  

3)泛型的通配符"?"  

   ?   是可以用任意类型替代。

   <?> 泛型通配符表示任意类型

   <? extends 类型>  表示这个类型是某个类型或接口的子类型。

   <? super 类型>    表示这个类型是某个类型的父类型。

 

  import java.util.*;

  import static java.lang.System.*;

  public class TestTemplate {

         public static void main(String[] args) {

                   List<Object> l1=new ArrayList<Object>();

                   List<String> l2=new ArrayList<String>();

                   List<Number> l3=new ArrayList<Number>();  //Number --- Object的子类,所有封装类的父类

                   List<Integer> l4=new ArrayList<Integer>();

                   List<Double> l5=new ArrayList<Double>();

                  

                   print(l1);

                   print(l2);

                   print(l3);

                   print(l4);

                   print(l5);

         }

       static void print(List<? extends Number> l){    //所有Number及其子类 l3,l4,l5通过

                   for(Number o:l){

                            out.println(o);

                   }

         }

       static void print(List<? extends Comparable> l){……}    //任何一个实现Comparable接口的类 l2,l4,l5通过

       static void print(List<? super Number> l){……}          //所有Number及其父类 l1,l3通过

      // "?"可以用来代替任何类型例如使用通配符来实现print方法。

       public static void print(GenList<?> list){……} //表示任何一种泛型

 

  }

 

4)泛型方法的定义 --- 相当于方法的模版

  把数组拷贝到集合时,数组的类型一定要和集合的泛型相同。

  <...>定义泛型,其中的"..."一般用大写字母来代替,也就是泛型的命名,其实,在运行时会根据实际类型替换掉那个泛型。

  在方法的修饰符和返回值之间定义泛型

  <E> void copyArrayToList(E[] os,List<E> lst){……}

  static <E extends Number & Comparable> void copyArrayToList(E[] os,List<E> lst){……}   //定义泛型的范围 类在前接口在后

  static<E , V extends E> void copyArrayToList(E[] os,List<E> lst){……} //定义多个泛型

  "super"只能用在泛型的通配符上,不能用在泛型的定义上

 

  import java.util.*;

  public class TestGenerics3 {

         public static void main(String[] args) {

                   List<String> l1=new ArrayList<String>();

                   List<Number> l2=new ArrayList<Number>();

                   List<Integer> l3=new ArrayList<Integer>();

                   List<Double> l4=new ArrayList<Double>();

                   List<Object> l5=new ArrayList<Object>();

                   String[] s1=new String[10];

                   Number[] s2=new Number[10];

                   Integer[] s3=new Integer[10];

                   Double[] s4=new Double[10];

                   Object[] s5=new Object[10];

                   copyFromArray(l1,s1);

                   copyFromArray(l2,s2);

                   copyFromArray(l3,s3);

                   copyFromArray(l4,s4);

                   copyFromArray(l5,s5);

                  

         }

         //把数组的数据导入到集合中

         public static <T extends Number&Comparable> void copyFromArray(List<T> l,T[] os){

                   for(T o:os){

                            l.add(o);

                   }

         }

  }

 

  受限泛型是指类型参数的取值范围是受到限制的. extends关键字不仅仅可以用来声明类的继承关系也可以用来声明类型参数(type parameter)的受限关系.

  泛型定义的时候,只能使用extends不能使用 super,只能向下,不能向上。

  调用时用<?>定义时用 <E>

 

5)泛型类的定义

  类的静态方法不能使用泛型,因为泛型类是在创建对象的时候产生的。

  class MyClass<E>{

         public void show(E a){

                   System.out.println(a);

         }

         public E get(){

                   return null;

         }

        

  }

 

  受限泛型

  class MyClass <E extends Number>{

         public void show(E a){

 

         }

  } 

 

3、注释

4、并发

 

 

四、反射 reflect

  反射,在运行时,动态分析或使用一个类进行工作。

  反射是一套API,是一种对底层的对象操作技术

1、类加载

  类加载,生成.class文件,保存类的信息 

  类对象,是一个描述这个类信息的对象,对虚拟机加载类的时候,就会创建这个类的类对象并加载该对象。

  Class,是类对象的类。称为类类。只有对象才会被加载到虚拟机中。一个类只会被加载一次。 

 

2、获得类对象的三种方式:(类对象不用new的方法得到的)

1)也可以用 类名.Class,获得这个类的类对象。

2)用一类的对象掉用a.getClass(),得到这个对象的类型的类对象。

3)也可以使用Class.forName(类名)Class类中的静态方法),也可以得到这个类的类对象,

  (注意,这里写的类名必须是全限定名(全名),是包名加类名,XXX.XXX.XXXX)。强制类加载,这种方法是经常使用的。

 

  一个类的类对象是唯一的。

  在使用Class.forName(类名)时,如果使用时写的类名的类,还没有被加载,则会加载这个类。

 

  Class c;

  c.getName(); 返回类名

  c.getSuperclass(); 这个方法是获得这个类的父类的类对象。

  c.getInterfaces(); 会获得这个类所实现的接口,这个方法返回是一个类对象的数组。

 

  方法对象是类中的方法的信息的描述。java.lang.reflect.Method,方法类的对象可以通过类对象的getMethods() 方法获得,

  获得的是一个方法对象的数组,获得类中的定义的所有方法对象,除了构造方法。

 

  构造方法对象,是用来描述构造方法的信息。java.lang.reflect.Constructor构造方法类的对象可以通过类对象的getConstructors()方法获得,

  获得这个类的所有构造方法对象。

 

  属性对象,使用来描述属性的信息。java.lang.reflect.Field属性类的对象对象可以通过类对象getFields() 这个方法是获得所有属性的属性对象。

 

作业:

1、通过运行时命令行参数输入一个类名,类出类中所有的方法

2、实现一个带泛型的堆栈,用来存放Number

3、实现一个栈,用来存放任意类型

  方法:遍历,pop,push,从数组拷贝

4、定义一个枚举,枚举值是课程,每个枚举值有个教师姓名的属性

                        CoreJava第十八天         2007-5-22

                       

一、反射              

1、获取方法和属性

   反射可以获取这个类中定义的方法和属性的信息,简单数据类型在使用反射时要转换成封装类。

   Class c = Teacher.class;

   Method m = c.getMethod("mrthod",int.class);

  

2、通过类对象生成类的对象

   Class c = Teacher.class;

   Object o = c.newInstance();

 

3、通过类对象调用方法  

   //1.get class Object

   Class c=Class.forName("Student");

   //2.get Constructor object

   Class[] cs={String.class};

   Constructor con=c.getConstructor(cs);//按照参数表来调用制定构造方法。

   //3.create object

   Object[] os={"liucy"};

   Object o=con.newInstance(os);

   //4.get method object

   String methodName="study";

   Class[] pcs={String.class};

   Method m=c.getMethod(methodName,pcs);//按照参数表来获得制定的方法对象。

   //5.invoke the method

   Object[] ocs={"EJB"};

   m.invoke(o,ocs);

  

  

二、CoreJava 5.0的注释

1、定义:Annotation描述代码的代码(区:描述代码的文字)

             给机器看         给人看的

2、注释的分类:

1)、标记注释:没有任何属性的注释。@注释名

2)、单值注释:只有一个属性的注释。@注释名(value=___)

在单值注释中如果只有一个属性且属性名就是value,则“value=”可以省略。

3)、多值注释:有多个属性的注释。多值注释又叫普通注释。

                                                        @注释名(多个属性附值,中间用逗号隔开)

3、内置注释:

1)、Override(只能用来注释方法)

                            表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。

2)、Deprecated

 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。

3)、SuppressWarnings(该注释无效)

指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。

 

4、自定义注释

自定义注释

    public  @interface Test{

   

    }

    在自定义注释时,要用注释来注释(描述)注释。

    @target(),用来描述(注释)注释所能够注释的程序员元素。

    @Retention(),描述(注释)注释要保留多久。

    注释的属性类型可以是

    8种基本类型

    String

    Enum

    Annotation

    以及它们的数组

 

5、注释的注释:java.lang. annotation包中

1)、Target:指示注释类型所适用的程序元素的种类。

         例:@Target(value = {ElementType.METHOD});

                            说明该注释用来修饰方法。

2)、Retention:指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为RetentionPolicy.CLASS

例:Retention(value = {RetentionPolicy.xxx}

    xCLASS表示保留到类文件中,运行时抛弃。

   xRUNTIME表示运行时仍保留

   xSOURCE时表示编译后丢弃。

3)、Documented:指示某一类型的注释将通过 javadoc 和类似的默认工具进行文档化。应使用此类型来注释这些类型的声明:其注释会影响由其客户端注释的元素的使用。

4)、Inherited:指示注释类型被自动继承。如果在注释类型声

明中存在 Inherited 元注释,并且用户在某一类声明中查询该注释类型,同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。

          注:在注释中,一个属性既是属性又是方法。

          

  标注:描述代码的文字

    描述代码的代码。给编译器看的代码,作用是规范编译器的语法。

    class Student{

      @Override

      public String toString(){

        return “student”;

      }

   

    }

   注释类型 java.lang

    1、标记注释(没有属性)

      @Override

    2、单值注释

      @注释名(a="liucy")

      @注释名(prameter=10

      int parameter

      特例:

      @注释名 (value=134 )

      等价于 @注释名 (134 )

    3.普通注释(多值注释)

    (key1=value,……)

      @__(a="liucy,b=10)

 

   @Override 只能放在方法前面

    @Deprecated 用于描述过期

    @SuppressWarnings 抑制警告

          @SuperessWarning({ddd,aaa,ccc})  //JVM还没有实现这个注释       

          

 

三、Java5.0中的并发

1、所在的包:Java.util.concurrent

2、重写线程的原因:

1)何一个进程的创建(连接)和销毁(释放资源)的过程都          是一个不可忽视的开销。

2run方法的缺陷:没有返回值,没有抛例外。

3、对比1.45.0的线程

        

       5.0                            1.4

ExecutorService          取代      Thread

Callable   Future      取代       Runnable

Lock                取代         Synchronized

SignalAll           取代      notifyAll()

await()                      取代         wait()

 

 

    三个新加的多线程包

   

    Java 5.0里新加入了三个多线程包:java.util.concurrent, java.util.concurrent.atomic,

   

    java.util.concurrent.locks.

    java.util.concurrent包含了常用的多线程工具,是新的多线程工具的主体。

    java.util.concurrent.atomic包含了不用加锁情况下就能改变值的原子变量,比如说AtomicInteger提供了addAndGet()方法。AddGet是两个不同的操作,为了保证别的线程不干扰,以往的做法是先锁定共享的变量,然后在锁定的范围内进行两步操作。但用AtomicInteger.addAndGet()就不用担心锁定的事了,其内部实现保证了这两步操作是在原子量级发生的,不会被别的线程干扰。

    java.util.concurrent.locks包包含锁定的工具。

   

    Callable  Future接口

   

    Executor接口替代了Thread类,他可以创建定量的和动态以及周期性的线程池。

    ExecutorService接口,线程池,用来存放线程来节省创建和销毁资源的消耗。

   

    Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。CallableRunnable有几点不同:

    Callable规定的方法是call(),而Runnable规定的方法是run().

    Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。

    call()方法可抛出异常,而run()方法是不能抛出异常的。

    

    Future对象可以获得线程运行的返回值

   

    运行Callable任务可拿到一个Future对象,通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。

    以下是Callable的一个例子:

    public class DoCallStuff implements Callable{ // *1

            private int aInt;

            public DoCallStuff(int aInt) {

                    this.aInt = aInt;

            }

            public String call() throws Exception { //*2

                    boolean resultOk = false;

                    if(aInt == 0){

                            resultOk = true;

                    }  else if(aInt == 1){

                            while(true){ //infinite loop

                                    System.out.println("looping....");

                                    Thread.sleep(3000);

                            }

                    } else {

                            throw new Exception("Callable terminated with Exception!"); //*3

                    }

                    if(resultOk){

                            return "Task done.";

                    } else {

                            return "Task failed";

                    }

            }

    }

    *1: 名为DoCallStuff类实现了CallableString将是call方法的返回值类型。例子中用了String,但可以是任何Java类。

    *2: call方法的返回值类型为String,这是和类的定义相对应的。并且可以抛出异常。

    *3: call方法可以抛出异常,如加重的斜体字所示。

    以下是调用DoCallStuff的主程序。

    import java.util.concurrent.ExecutionException;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.Future;

    public class Executor {

            public static void main(String[] args){

                    //*1

                    DoCallStuff call1 = new DoCallStuff(0);

                    DoCallStuff call2 = new DoCallStuff(1);

                    DoCallStuff call3 = new DoCallStuff(2);

                    //*2

                    ExecutorService es = Executors.newFixedThreadPool(3);

                    //*3

                    Future future1 = es.submit(call1);

                    Future future2 = es.submit(call2);

                    Future future3 = es.submit(call3);

                    try {

                            //*4

                            System.out.println(future1.get());

                             //*5

                            Thread.sleep(3000);

                            System.out.println("Thread 2 terminated? :" + future2.cancel(true));

                            //*6

                            System.out.println(future3.get());

                    } catch (ExecutionException ex) {

                            ex.printStackTrace();

                    } catch (InterruptedException ex) {

                            ex.printStackTrace();

                    }

            }

    }

    *1: 定义了几个任务

    *2: 初始了任务执行工具。任务的执行框架将会在后面解释。

    *3: 执行任务,任务启动时返回了一个Future对象,如果想得到任务执行的结果或者是异常可对这个Future对象进行操作。Future所含的值必须跟Callable所含的值对映,比如说例子中Future对印Callable

    *4: 任务1正常执行完毕,future1.get()会返回线程的值

    *5: 任务2在进行一个死循环,调用future2.cancel(true)来中止此线程。传入的参数标明是否可打断线程,true表明可以打断。

    *6: 任务3抛出异常,调用future3.get()时会引起异常的抛出。

     运行Executor会有以下运行结果:

    looping....

    Task done. //*1

    looping....

    looping....//*2

    looping....

    looping....

    looping....

    looping....

    Thread 2 terminated? :true //*3

    //*4

    java.util.concurrent.ExecutionException: java.lang.Exception: Callable terminated with Exception!

            at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:205)

            at java.util.concurrent.FutureTask.get(FutureTask.java:80)

            at concurrent.Executor.main(Executor.java:43)

            …….

    *1: 任务1正常结束

    *2: 任务2是个死循环,这是它的打印结果

    *3: 指示任务2被取消

    *4: 在执行future3.get()时得到任务3抛出的异常

你可能感兴趣的:(Java学习笔记6)