首页 > 代码库 > java强化篇(四)---注解、泛型。类加载器.线程
java强化篇(四)---注解、泛型。类加载器.线程
Java的三个基本注解
@Deprecated :标记此程序元素已过时,通常是因为它很危险或存在更好的选择。
@SuppressWarnings :取消显示指定的编译器警告。
@Override :表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。
元注解@Retention--定义注解类的存在区域,有三种取值:RetentionPolicy.SOURCE、RetentionPolicy.CLASS、 RetentionPolicy.RUNTIME,分别对应java源文件、class文件、内存中的字节码。
元注解@Target:确定注解类可用范围,取值ElementType.METHOD...
定义基本类型的属性
在注解类中增加String color();
引用:@MyAnnotation(color=“red”)
用反射获得注解对应的实例对象后,再通过对象调用属性对应的方法。
MyAnnotation a=(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation)
System.out.println(a.color);
为属性定义缺省值String color()default “red”;
Value属性 String value();
如果一个 注解只有一个名称为value的属性,且你只想设置value的属性,可以省略,@MyAnnotation(“red”);
泛型
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及的术语:
1、 整个ArrayList<E>称为泛型类型
2、ArrayList<E>中E称为类型变量或类型参数
3、整个ArrayList<Integer>称为参数化的类型
4、ArrayList<Integer>中的Integer叫类型参数的实例或实际类型参数
5、ArrayList<Integer>中的<>念typeof
6、ArrayList称为原始类型
参数化类型与原始类型的兼容性-编译警告
Collection<String> = new Vector();
Collection = new Vector<String >();
参数化类型不考虑类型参数的继承关系
Vector<String> v = new Vector<Object>(); //错!
Vector<Object> v = new Vector<String>();//错!
在创建数组实例时,数组的元素不能使用参数化的类型。
定义泛型类
类中的多个方法要使用同一个泛型参数,此时要定义类级别的泛型。
public class GenericDao<T>{
private T field1;
public void save<T obj>{}
public T getById(int ID){}
}
类级别的泛型是根据引用类名指定的泛型信息来参数化变量的.
GenericDao<String> dao=null;
new GenericDao<String>();
注意
1、在对泛型参数化时,必须是引用类型,不能是基本类型。
2、当一个类被声明为泛型时,只能被实例变量和方法调用(还有内嵌类),而不能被静态变量和静态方法调用。因为类静态成员是被所有参数化的类所共享的,所以静态成员不应该拥有类级别的类型参数。
通过反射获得泛型的参数化类型
Vector<Date> v = newVector<Date>();
通过v.getClass()是无法获得泛型的参数化类型的。
将其传递给一个方法,可实现此功能。
public static void applyVector(Vector<Date> v){}
Method applyMethod =GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
System.out.println(pType.getRawType());//Vector
System.out.println(pType.getActualTypeArguments()[0]);//Date
类加载器之间的父子关系和管辖范围图
当java虚拟机加载类时,到底用哪个类加载器?
◇首先当前线程的类加载器去加载线程中的第一个类
◇如果类A引用了类B,那么java虚拟机将使用加载类A的类加载器去加载类B
◇还可以直接调用ClassLoader.loaderClass()方法来指定某个类加载器去加载
每个类加载器加载时,又委托给其上级的类加载器。
当所有祖宗类加载器没有加载到该类,则回到发起者类加载器,还加载不到,则抛出ClassNoFoundException,不是再找发起者类加载器和儿子,因为没有getChild方法。——从上到下的加载。
自定义类加载器
工作机制
父类——>loadClass/findClass()/得到class文件的内容转换成字节码—>difineClass()/将一个 byte 数组转换为 Class 类的实例
实现步骤
◇自定义的类加载器必须继承ClassLoader
◇覆盖findClass方法
◇覆盖difineClass()方法
传统线程机制的回顾
创建线程的两种传统方式
在Thread子类覆盖的run方法中编写运行代码
涉及一个以往知识点:能否在run方法声明上抛出InterruptedException异常,以便省略run方法内部对Thread.sleep()语句的try…catch处理?
在传递给Thread对象的Runnable对象的run方法中编写代码
总结:查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。
问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么,线程运行时的执行代码是子类的run方法的代码?还是Runnable对象的run方法的代码?
涉及到的一个以往知识点:匿名内部类对象的构造方法如何调用父类的非默认构造方法。
定时器的应用
Timer类
TimerTask类
多个线程访问共享对象和数据的方式
如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。
如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。
极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。
java强化篇(四)---注解、泛型。类加载器.线程