java高新技术知识

本文介绍了Java集成开发环境、Java企业版、Java消息服务等核心概念,对比了workspace与project、Perspective与view的区别,详细讲解了Java代码模板、工程导入、jdk1.5新特性等内容,并深入探讨了反射机制、泛型、类加载器、动态代理等高级主题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、名称解释:
 IDE:integrate develop environment,集成开发环境。
 JAVA EE:Java Platform,Enterprise Edition,java企业级应用程序版本。
 JMS:Java Message Service,java消息服务。Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
 JMX:Java Management Extensions,java管理扩展。是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
 JNDI:Java Naming and Directory Interface,java命名和目录接口。是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。

2、workspace与project
 File/SwitchWorkspace,可切换工作区间。
 Window/Preferences下的全局配置对指定workspace有效。
 工程名/Properties下的局部配置对指定工程有效。
 修改全局配置会自动修改局部配置,单独修改局部配置会覆盖全局配置。
 
3、Perspective与view
 Perspective指透视图,如java,debug等,每个透视图默认会打开指定view,Window/Show View可配置某个透视图中打开的view。
 
4、设置工程的javac和java
 Window/Preference/Java/Compiler,配置javac,
 Window/Preference/Java/Installed JREs,配置java。
 注意:
 高版本的java能运行低版本的javac编译的程序,反之不能,应确保java版本等于或高于javac版本。
 
5、快捷键的绑定与代码模板
 1>快捷键的绑定:
 Window/General/Keys,可配置各个功能快捷键。如Content Assist的binding为Alt+/。修改快捷键时,应先Unbind Command解除绑定,再在Binding输入框中按指定快捷键,而不是拼写快捷键的字母。
 注意:快捷键冲突时将不起效果,应该先将某个快捷键解除绑定另一个快捷键才会生效。
 2>设置代码模板:
 Window/Java/Editor/Templates,新建模板:Name为tryFinally,Pattern为:
 try{
  ${line_selection}
 }
 finally{
  ${cursor}
 }
 编写java代码时,选中需要try的代码,右键Surround With/tryFinally,就可以为选中代码添加模板,并将光标移动到指定位置。
 
6、导入工程
 右键/Import/Existing Projects into WorkSpace,选中某工程,即可导入。
 替换工程引用库:右键/Properties/Java Build Path/Library。
 自定义用户库:右键/Properties/Java Build Path/Library/Add Library/User Library/next/User Library/New/Add JARs。配置好之后,就可以一次为工程导入多个jar文件。
 
7、jdk 1.5新特性
 1>静态导入
 import static java.lang.System.out;//导入java.lang包下的System类的静态方法out;
 public class HelloWorld{
     public static void main(String[] args){
         out.print("Hello World!");//既是在这里不用再写成System.out.println("Hello World!")了,因为已经导入了这个静态方法out。
     }
 }
 import xxxx 和 import static xxxx的区别是前者一般导入的是类文件如import java.util.Scanner;后者一般是导入静态的方法,import static java.lang.System.out;
 2>可变参数
 如果一个方法接受的参数个数不固定,就需要重载方法多次以接收不同个数的参数,为此jdk 1.5提出可变参数概念。
 可变参数的特点:
 只能出现在参数列表的最后。
 书写方法:...位于变量类型和变量名之间,前后有无空格都可以。
 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
 3>增强for循环
 语法:
 for(type 变量名:集合变量名){...}
 注意事项:
 迭代变量必须在()中定义。
 集合变量可以是数组或实现了iterable接口的集合类。
 举例:
 public static int add(int x, int... args)
 {
  int sum = x;
  for(int arg : args)
  {
   sum += arg;
  }
  return sum;
 }
 4>基本数据类型的自动装箱与拆箱
 自动装箱:
 Integer num1 = 3;
 自动拆箱:
 System.out.println(num + 5);
 基本数据类型的对象缓存:
 Integer num1 = 5;
 Integer num2 = 5;
 System.out.println(num1 == num2);//true,一个字节长度内即-128~127会缓存起来,下次直接从内存中调用,这也是享元模式:将经常用到的数据缓存起来,而不是每次都新建一个对象,以节省空间提高效率。
 Integer num3 = 128;
 Integer num4 = 128;
 System.out.println(num3 == num4);//false
 Integer num5 = Integer.valueOf(5);
 Integer num6 = Integer.valueOf(5);
 System.out.println(num5 == num6);//true,和自动装箱机制一样
 5>枚举
 枚举就是让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法做到这一点。
 枚举是一个特殊的类,其中的每个元素都是该类的一个实例对象,其中也可以定义构造方法、成员变量、普通方法、抽象方法。
 枚举元素必须放在枚举体的最开始部分,枚举元素列表的后面要有分号与其他元素分隔,否则编译器报错。
 带构造方法的枚举:
 构造方法必须定义为私有的;
 枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
 枚举中每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
 枚举只有一个成员时,就可以作为一种单例的实现方式。
 public enum WeekDay {
  SUN(1), MON(), TUE, WED, THI, FRI, SAT;
  private WeekDay() {
   System.out.println("first");
  }

  private WeekDay(int day) {
   System.out.println("second");
  }
 }

 public enum TrafficLamp {
  RED(30) {
   public TrafficLamp nextLamp() {
    return GREEN;
   }
  },
  GREEN(20) {
   public TrafficLamp nextLamp() {
    return YELLOW;
   }
  },
  YELLOW(10) {
   public TrafficLamp nextLamp() {
    return RED;
   }
  };
  public abstract TrafficLamp nextLamp();

  private int time;

  private TrafficLamp(int time) {
   this.time = time;
  };
 }
 
8、反射的基础---Class
 Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class。
 一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是字节码,不同的类的字节码是不同的,这一个个的空间分别用一个个的对象来表示,这些对象显示具有相同的类型。
 如何得到各个字节码对应的实例对象:
 类名.class,例如:System.class;
 对象.getClass(),例如:new Date().getClass();
 Class.forName(类名),例如:Class.forName("java.util.Date");
 九个预定义的Class实例对象:
 八个基本类型、void
   String str1 = "abc";
  Class cls1 =str1.getClass();
  Class cls2 = String.class;
  Class cls3 = Class.forName("java.lang.String");
  System.out.println(cls1 == cls2);//true
  System.out.println(cls1 == cls3);//true
  System.out.println(cls1.isPrimitive());//false
  System.out.println(int.class.isPrimitive());//true
  System.out.println(int.class == Integer.class);//false
  System.out.println(int.class == Integer.TYPE);//true
 数组类型的Class类型对象:
 Class.isArray();
 总之,只要在源程序中出现的类型,都有各自的Class实例对象,例如:int[],void。
 
9、反射
 反射就是把java类中的各种成分映射成相应的java类。
 1>Constructor类:
 Constructor类代表某个类中的一个构造方法。
 得到某个类的所有构造方法
 Constructor[] constructors = Class.forName("java.lang.string").getConstructors();
 得到某个类的某个构造方法
 Constructor constructor = Class.forName("java.lang.string").getConstructor(StringBuffer.class);
 创建实例对象:
 通常方式:
 String str = new String(new StringBuffer("abc"));
 反射方式:
 Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
 String str1 = (String) constructor1.newInstance(new StringBuffer("abc"));//得到构造器需要传递指定类型,调用构造器创建实例时也需要传递相应类型,如果此处参数为“abc”,将抛出参数不匹配异常。
 Class.newInstance()方法:
 String str2 = (String)Class.forName("java.lang.String").newInstance();
 该方法首先得到默认的构造方法,然后用该构造方法创建实例对象,用缓存机制来保存默认构造方法的实例对象,所以反射比较消耗性能。
 2>Field类:
 Filed类代表某个类中的一个成员变量。
 ReflectPoint rp1 = new ReflectPoint(3,5);
 //Field为类上的成员变量,而不是指对象上的成员变量
 Field fieldY = rp1.getClass().getField("y");
 System.out.println(fieldY.get(rp1));
 //获取私有属性值
 Field fieldX = rp1.getClass().getDeclaredField("x");
 fieldX.setAccessible(true);//设置属性可访问
 System.out.println(fieldX.get(rp1));
 3>Method类:
 Method类代表某个类中的一个成员方法。
 得到类中的某个方法:
 Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
 调用方法:
 通常方式
 System.out.println(str.charAt(1));
 反射方式
 System.out.println(charAt.invoke(str, 1));
 注意:如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法。
 4>用反射方式执行某个类中的main方法:
 问题:
 启动java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式调用main方法时,如何为invoke方法传递数组?按jdk1.5的语法,整个数据就是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,jdk为实现向下兼容,要按照jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。所以在给main方法传递参数时,不能使用代码mainMethod。invoke(null,new String[]{"xxx"}),此时会出现参数类型不对的问题。
 解决办法:
 class TestArgument
 {
  public static void main(String[] args)
  {
   for(String arg : args)
   {
    System.out.println(arg);
   }
  }
 }
 //通常方式
 TestArgument.main(new String[]{"111","222","333"});//反射方式
 Method mainMethod = Class.forName("cn.itcast.day1.TestArgument").getMethod("main", String[].class);
 //以下语句将抛出非法参数异常
 //mainMethod.invoke(null, new String[]{"111","222","333"});
 //以下语句将字符串数组包装成一个对象,将其打包传入main方法将避免异常
 mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
 mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
 5>数组的反射:
 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
 基本类型的一维数组可以被当做Object类型使用,但不能当做Object[]类型使用,非基本类型的一维数组既可以当做Object类型使用,也可以当做Object[]类型使用。
 Arrays.asList()方法处理int[]和String[]时的差异:asList()方法形参为Object[],传入int[]时将其作为一个object整体,而传入String[]可解析为一个object[]数组。
 Array工具类用于完成对数组的反射操作。
 获取参数的类型:
   Object[] argument = new Object[]{1,"abc"};
   System.out.println(argument[0].getClass().getName());//输出java.lang.Integer
   int[] a1 = new int[]{1,2,3};
  int[] a2 = new int[4];
  int[][] a3 = new int[3][4];
  String[] a4 = new String[]{"a","b","c"};
  System.out.println(a1.getClass() == a2.getClass());//true
  //以下两条语句编译不通过
//  System.out.println(a1.getClass() == a3.getClass());//false
//  System.out.println(a1.getClass() == a4.getClass());//false
  System.out.println(a1.getClass().getName());//输出[I
  System.out.println(a1.getClass().getSuperclass().getName());//输出java.lang.Object
  System.out.println(a4.getClass().getSuperclass().getName());//输出java.lang.Object
  
  Object obj1 = a1;
  Object obj2 = a3;
  Object obj3 = a4;
//  Object[] obj4 = a1;//编译不通过,int是原始数据类型,所以int数组不是object数组
  Object[] obj5 = a3;
  Object[] obj6 = a4;
  
  System.out.println(a1);//输出[I@7c6768
  System.out.println(a4);//输出[Ljava.lang.String;@7c6768
  System.out.println(Arrays.asList(a1));//输出[[I@1f33675]
  System.out.println(Arrays.asList(a4));//输出[a, b, c]
  
  //对象为数组时打印数组中各个元素,否则打印对象本身
  private static void printObject(Object obj)
 {
  Class clazz = obj.getClass();
  if(clazz.isArray())
  {
   int len = Array.getLength(obj);
   for(int i=0; i<len; i++)
   {
    System.out.println(Array.get(obj, i));
   }
  }
  else
  {
   System.out.println(obj);
  }
 }
 
10、ArrayList和HashSet的比较,HashCode的分析
  哈希算法:将集合分成若干存储区域,每个对象可以计算出一个哈希码,将哈希码分组,每组对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域。
  HastSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域。Object类中定义了一个hashCode()方法来返回每个java对象的哈希码,当从HashSet集合中查找某个对象时,java系统首先调用对象的hashCode()方法获得该对象的哈希码,然后根据哈希码找到对应的存储区域,最后取出该存储区域的每个元素和该对象进行equals()方法比较,这样不用遍历集合中所有元素就可以得到结论,由此可见,HashSet集合具有很好的对象检索性能。但HashSet集合存储对象的效率相对要低些。
  ReflectPoint pt1 = new ReflectPoint(3,3);
  ReflectPoint pt2 = new ReflectPoint(5,5);
  ReflectPoint pt3 = new ReflectPoint(3,3);
  
  Collection<ReflectPoint> arrayList = new ArrayList<ReflectPoint>();
  arrayList.add(pt1);
  arrayList.add(pt2);
  arrayList.add(pt3);
  arrayList.add(pt1);
  System.out.println(arrayList.size());//输出4
  
  Collection<ReflectPoint> hashSet = new HashSet<ReflectPoint>();
  hashSet.add(pt1);
  hashSet.add(pt2);
  hashSet.add(pt3);
  hashSet.add(pt1);
  System.out.println(hashSet.size());//输出2
  
  pt1.y = 7;//属性值改变导致hashSet值改变,存储位置发生改变,无法删除,数据量大时将导致内存泄露
  hashSet.remove(pt1);
  System.out.println(hashSet.size());//输出2
  
11、反射的作用---实现框架功能
  框架和工具类的区别:工具类是被用户的类调用,而框架则是调用用户提供的类。框架、用户类、工具类的关系犹如房子、门窗、锁的关系。
  反射的作用:因为在写某些程序如框架时,无法知道被调用的类名,所以在程序中无法直接new某个类的实例对象,这就需要用到反射。
  InputStream ips = new FileInputStream("config.properties");
  Properties props = new Properties();
  props.load(ips);//加载文件中所有键值对
  String className = props.getProperty("className");
  Collection arrayList = (Collection) Class.forName(className).newInstance();//利用反射根据传入的名字创建类的实例对象,在此无需知道具体类名
  
12、用类加载器的方式管理资源和配置文件
  InputStream ips1 = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/resource/config.properties");//绝对路径获取文件,路径前面不能加“/”
  InputStream ips2 = ReflectTest2.class.getResourceAsStream("resource/config.properties");//相对路径获取文件
  InputStream ips3 = ReflectTest2.class.getResourceAsStream("cn/itcast/day1/resource/config.properties");//绝对路径获取文件,路径前可加也可不加“/”
  
13、内省,javabean
 javabean是一种特殊的java类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
 如果要在两个模块之间传递多个信息,可以将这些信息封装到一个javabean中,这种javabean对象的实例对象通常称之为值对象(Value Object,简称VO)。
 一个类被当做javabean使用时,javabean的属性是根据方法名推断出来的,因为外部看不到javabean内部的成员变量。javabean的属性名称是set方法去掉set前缀,如果剩余部分第二个字母是小写的,则把剩余部分的首字母改成小写。例如:
 setId()的属性名为id;
 setCPU()的属性名为CPU;
 //mainMethod.invoke(null, new String[]{"111","222","333"});
 开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性。
 内省访问JavaBean属性的两种方式:
 1>通过PropertyDescriptor类操作Bean的属性。
 2>通过Introspector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
 对javabean的简单内省操作:
  ReflectPoint rp = new ReflectPoint(3,3);
  PropertyDescriptor pd = new PropertyDescriptor("x", rp.getClass());//获取属性描述对象
  Method methodGetX = pd.getReadMethod();//获取get方法
  System.out.println(methodGetX.invoke(rp));//输出3
  Method methodSetX = pd.getWriteMethod();//获取set方法
  methodSetX.invoke(rp, 7);
  System.out.println(rp.getX());//输出7
  
14、BeanUtils工具包
   将一个jar包直接导入某个工程时,如果工程移动,可能导致找不到jar包,方便的做法是:工程下新建一个文件夹,存放jar包,再右键build path加载到构建路径下。
   BeanUtils类用于设置获取属性的值,get属性时返回的结果为字符串,set属性时可以接受任意类型的对象,通常使用字符串。
   PropertyUtils类也用于设置获取属性的值,get属性时返回的是该属性本来的类型,set属性时也只接受该属性本来的类型。当设置属性值参数类型不对时,将抛出参数类型不匹配异常。
   综上,当不知道参数类型时,建议使用BeanUtils设置属性值。
  
15、注解定义及几个基本注解
  注解相当于一个标记,javac编译器,开发工具和其他程序可以用反射来了解类及各种元素上有无各种标记,根据标记类型,去做相应处理。
  注解可以加在包,类,字段,方法,方法的参数及局部变量上。
  几个基本注解:
  @SuppressWarnings("deprecation") 抑制警告
  @Deprecated 过时的
  @Override 覆盖
  @Retention元注解,三种取值:
  RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME,分别对应:
  java源文件,class文件,内存中的字节码
  @Target元注解:设置注解可应用在哪些元素上,默认值为任何元素。

16、为注解增加基本属性
  1>定义基本类型的属性:
  在注解类中增加String color();
  在使用注解类的类中增加@MyAnnotation(color="red")
  2>用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法:
  MyAnnotation annotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
  System.out.println(annotation.color());
  3>为属性指定缺省值:
  String color default "yellow";
  4>设置属性值:
  如果注解类中包含多个属性,那么使用此注解类时只需设置没有默认值的属性值,否则新值将覆盖默认值。
  5>数组类型的属性:
  int[] arrayAttr() default {1,2,3}
  @MyAnnotation(arrayAttr={4,5,6})
  如果数组属性中只有一个元素,属性值部分可以省略大括号。
  6>枚举类型的属性:
  TrafficLamp lamp();
  @MyAnnotation(lamp=TrafficLamp.GREEN)
  7>注解类型的属性:
  MetaAnnotation annotationAttr() default @MetaAnnotation("xxx");
  @MyAnnotation(annotationAttr=@MetaAnnotation("yyy"))
  调用方式:
  MetaAnnotation ma = myAnnotation.annotationAttr();
  system.out.println(ma.value);
  
17、泛型
  泛型就是在定义集合时,明确指定要向集合中添加的元素是哪种类型,无法加入指定类型以外的数据。
  泛型是提供给javac编译器使用的,可以限定集合中中的输入类型,让编译器挡住源程序的非法输入,编译器编译带类型说明的集合时会取出“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中假如其他类型的数据,例如,利用反射得到集合,再调用其add方法即可。
  ArrayList<String> collection1 = new ArrayList<String>();
  ArrayList<Integer> collection2 = new ArrayList<Integer>();
  System.out.println(collection1.getClass() == collection2.getClass()); //输出true
  Method addMethod = collection2.getClass().getMethod("add", Object.class);
  addMethod.invoke(collection2, "abc");//将字符串数据添加到泛型类型为整数的集合中
  System.out.println(collection2.get(0));//输出abc
  参数化类型与原始类型的兼容性:
  参数化类型可以引用一个原始类型的对象,编译器报警告,例如:
  Collection<String> c1 = new Vector();
  原始类型可以引用一个参数化类型的对象,编译器报警告,例如:
  Collection c2 = new Vector<String>();
  参数化类型不考虑类型参数的继承关系:
  Vector<Object> c3 = new Vector<String>();//错误
  Vector<String> c4 = new Vector<Object>();//错误
  Vector c5 = new Vector<String>();
  Vector<Object> c6 = c5;//正确,javac编译器是逐行编译的
  在创建数组实例时,数组的元素不能使用参数化的类型,例如:
  Vector<String> vectorList[] = new Vector<String>[10];//错误
  
18、泛型中的?通配符
  public static void printCollection(Collection<?> collection)
  {
   collection.add("abc");//错误
   collection.size();//正确,与参数类型无关
   for(Object obj : collection)//正确,任何类型都能转化为Object类型
   {
    System.out.println(obj);
   }
  }
  使用?通配符可以引用各种参数化的类型,?通配符定义的变量主要用作引用,可以调用参数化无关的方法,不能调用与参数化有关的方法。
  通配符的扩展:
  限定通配符的上边界:
  Vector<? extends Number> v1 = new Vector<Integer>();
  Vector<? extends Number> = new Vector<String>();//错误,不能进行类型转换
  限定通配符的下边界:
  Vector<? super Integer> v3 = new Vector<Number>();
  Vector<? super Integer> v3 = new Vector<Byte>();//错误,不能进行类型转换
  提示:限定通配符总是包括自己。
  Map泛型集合的迭代示例:
  HashMap<String, Integer> map1 = new HashMap<String, Integer>();
  map1.put("zhangsan", 10);
  map1.put("lisi", 20);
  map1.put("wangwu", 30);
  Set<Map.Entry<String, Integer>> entrySet = map1.entrySet();//获取map中的实体对象集合
  for(Map.Entry<String, Integer> entry : entrySet)
  {
   System.out.println(entry.getKey()+":"+entry.getValue());
  }
  jsp页面的集合迭代示例:
  <c:forEach items="${map}" var="entry">
   ${entry.key}:${entry.value}
  </c:forEach>
  
19、自定义泛型
  示例一:
  public static <T> T add(T x, T y)//用于放置泛型的类型参数的尖括号应出现在返回类型之前和其他所有修饰符之后,通常用单个大写字母表示。
  {
   return x+y;//错误,T类型可能不支持加法
  }
  Number x1 = add(1.2,2);
  Float x2 = add(1.2,2);//错误,必须是两个参数的交集类型
  Object x3 = add(1,"abc");
  String x4 = add(1,"abc");//错误
  示例二:
  public static <T> void swap(T[] a, int i, int j)
  {
   T temp = a[i];
   a[i] = a[j];
   a[j] = temp;
  }
  swap(new String[]{"aa","bb","cc"},1,2);
  swap(new int[]{1,2,3,4},2,3);//错误,实际类型只能是引用类型,不能是基本类型,由于int[]已经是引用类型,不能进行装箱。
  普通方法、构造方法和静态方法中都可以使用泛型。
  可用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但不能用于catch子句中。
  public static <T extends Exception> void genericException() throws T
  {
   try
   {
    
   }catch(Exception e)//此处不能定义为T
   {
    throw (T)e;
   }
  }
  
20、自定义泛型类型参数的类型推断
  编译器判断泛型方法的实际类型参数的过程称为类型推断。
  根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
  1>当某个类型变量只在整个参数列表中的所有参数和返回值的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,例如:
  swap(new String[3],3,4)---static <E> void swap(E[] a, int i, int j)
  2>当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型,这时对应此类型,例如:
  add(1,2)---static <T> T add(T a,T b)
  3>当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应了不同的类型,且没有使用返回值,这时取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了。
  fill(new Integer[3], 3,5f)---static <T> void fill(T[] a, T v)
  4>当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应了不同的类型,且有使用返回值,这时优先考虑返回值的类型。例如:
  Number x = add(3,1.5f)---static <T> T add(T a, T b)
  5>参数类型的类型推断具有传递性
  copy(new Integer[5], new String[5])---static <T> void copy(T[] a,T[] b) //正确,推断实际类型参数为Object;
  copy(new Vector<String>(),new Integer[5])---static <T> void copy(Collection<T> a, T[] b) //错误,根据参数化的Vector类实例将类型变量直接确定为String类型;
  
21、定义泛型类型
  如果类的实例对象中的多处都要用到同一个泛型参数,这时需要采用泛型类型的方法进行定义,也就是类级别的泛型,语法格式如下:
  public class GenericDao<E>
  {
   private E field1;
   public <E> void add(E person)
   {
   }
   public <E> E findById(int id)
   {
    return null;
   }
  }
  类级别的泛型是根据引用该类型时指定的参数信息来参数化类型变量的,例如:
  GenericDao<String> dao = new GenericDao<String>();
  注意:
  在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
  当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
  
22、通过反射获取泛型的参数化类型
  方法定义:
  public static void applyVector(Vector<Date> v1)
  {
  }
  获取示例:
  Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
  Type[] types = applyMethod.getGenericParameterTypes();
  ParameterizedType type = (ParameterizedType) types[0];
  System.out.println(type.getRawType());//输出class java.util.Vector
  System.out.println(type.getActualTypeArguments()[0]);//输出class java.util.Date
  
23、类加载器 
  java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:
  BootStrap,ExtClassLoader,AppClassLoader
  类加载器本身也是java类,因此java类加载器本身也需要被类加载器加载,BootStrap根加载器是C++编写。
  java虚拟机中的所有类装载器采用父子关系的属性结果进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
  类加载器之间的父子关系和管辖范围:
  BootStrap---JRE/lib/rt.jar
   |
   |
  ExtClassLoader---JRE/lib/ext/*.jar
   |
   |
  AppClassLoader---CLASSPATH指定的所有jar或目录
   |
   |
  MyClassLoader---用户指定的目录
  类加载器的委托机制:
  首先当前线程的类加载器去加载线程中的第一个类;
  如果类A引用了类B,java虚拟机使用加载类A的加载器加载类B;
  还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器进行加载。
  每个类加载器加载类时,先委托给其上级类加载器;
  当所有父加载器没有加载到类,回到发起者类加载器,如果还加载不了,则抛出ClassNotFoundException异常,而不会交给子级类加载器进行加载。
  注意:
  如果某个类被打包到JRE/lib/ext/*.jar目录下,但其父类并没有放到此目录,则抛出类找不到异常。
  ClassLoader loader = ClassLoaderTest.class.getClassLoader();
  while(null != loader)
  {
   System.out.println(loader.getClass().getName());
   loader = loader.getParent();
  }
  System.out.println(loader);
  输出:
  sun.misc.Launcher$AppClassLoader
  sun.misc.Launcher$ExtClassLoader
  null //BootStrap类加载器不是java类,而是C++二进制码,因此输出null
  
24、自定义类加载器
  自定义类加载器必须继承ClassLoader类;
  重写loadClass方法与findClass方法;
  重写defineClass方法;
  自定义加载器代码示例:
  public class MyClassLoader extends ClassLoader{
   private String classDir;
   public MyClassLoader()
   {
    
   }
   
   public MyClassLoader(String classDir)
   {
    this.classDir = classDir;
   }
   
   public static void main(String[] args) throws Exception
   {
    String srcPath = args[0];
    String destDir = args[1];
    FileInputStream fis = new FileInputStream(srcPath);
    String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
    String destPath = destDir + "\\" + destFileName;
    FileOutputStream fos = new FileOutputStream(destPath);
    cypher(fis, fos);
    fis.close(); 
    fos.close();
   }
   
   public static void cypher(InputStream ips, OutputStream ops) throws Exception
   {
    int b = -1;
    if(ips.read() != -1)
    {
     ops.write(b^0xff);
    }
   }
  
   @Override
   protected Class<?> findClass(String name) throws ClassNotFoundException {
    String classFileName = classDir + "\\" + name + ".class";
    try {
     FileInputStream fis = new FileInputStream(classFileName);
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
     cypher(fis, bos);
     fis.close();
     byte[] bytes = bos.toByteArray();
     return defineClass(bytes, 0, bytes.length);
    } catch (Exception e) {
     e.printStackTrace();
    }
    
    return super.findClass(name);
   } 
  }
  调用示例:
  Class clazz = new MyClassLoader("itcastlib").findClass("ClassLoaderAttachment");
  Date d1 = (Date) clazz.newInstance();
  System.out.println(d1);
  
25、代理的概念与作用
  如果要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如异常处理,日志,计算方法的运行时间,事务处理等等,这将用到代理类。
  如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中是使用目标类还是代理类,以后很容易切换。
  交叉业务的编程即为面向方面编程(Aspect oriented program,简称AOP),AOP的目标就是使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。
  代理是实现AOP功能的核心和关键技术。
  动态代理:
  要为系统中的各个接口的类增加代理功能,需要很多的代理类,全部采用静态代理类,将是件很麻烦的事情,JVM可以在运行期间动态的生成类的字节码,即动态代理。
  JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
  CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB。
  代理类的各个方法通常调用目标的相应方法和返回目标返回的结果外,还可以在代理方法的如下位置增加系统功能代码:
  1>在调用目标方法之前
  2>在调用目标方法之后
  3>在调用目标方法前后
  4>在调用目标方法 异常的catch块中
  创建动态类的实例对象:
  1>用反射获取构造方法;
  2>编写一个最简单的InvocationHandle类;
  3>调用构造方法创建类的实例对象,并将编写的InvocationHandle类的实例对象传进去;
  // 获取动态类的构造方法,需要传入InvocationHandler对象
  Constructor constructor = clazzProxy
    .getConstructor(InvocationHandler.class);
  class myInvocationHandler1 implements InvocationHandler {
   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
     throws Throwable {
    return null;
   }
  }
  Collection proxy1 = (Collection) constructor
    .newInstance(new myInvocationHandler1());
  System.out.println(proxy1);
  proxy1.clear();
  用Proxy.newProxyInstance()方法直接创建代理对象:
  Collection proxy3 = (Collection) Proxy.newProxyInstance(
    Collection.class.getClassLoader(),
    new Class[] { Collection.class },
    new InvocationHandler() {
     ArrayList target = new ArrayList();
     @Override
     public Object invoke(Object proxy, Method method,
       Object[] args) throws Throwable {
      long beginTime = System.currentTimeMillis();
      Object retVal = method.invoke(target, args);
      long endTime = System.currentTimeMillis();
      System.out.println(method.getName() + "running time is:" + (endTime - beginTime));
      return retVal;
     }
    });
  proxy3.add("zhangsan");
  proxy3.add("lisi");
  System.out.println(proxy3.size());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值