41.自定义泛型方法的练习与类型推断总结
类型参数的类型推断
编译器判断泛型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现在方法是一种非常复杂的过程.
根据调用泛型方法时实际传递的参数类型或返回值类型来推断,具体规则土下:
当某个类型变量只在整个参数列表中的所有参熟和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) -> static <E> void swap(E[]a,int i,int j)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) -> static <T> T add(T a,T b)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) -> static <T> void fill(T[]a,T v)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译器将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x=(3,3.5f) -> static <T> T add(T a,T b)
参数类型的类型推断具有传递性,下面第一种情况推断实际参熟类型为Object,编译没有问题,而第二中情况则根据参熟化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5] -> static <T> void copy(T[] a,T[] b);
42.自定义泛型类的应用
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:public class GenericDao<T>{
private T field1;
public void save(T obj){}
public T getByld(int id){}
}
类级别的泛型是根据引用该类名时指定的类型信息来参数话类型变量的,例如,如下两种方式都可以:
GenericDao<String>dao = null;
new genericDao<String>();
注意:
在对泛型类型进行参数化时,类型参数的实例必须是要引用类型,不能是基本类型.
在一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用.因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数.
43.通过反射获得泛型的实际类型参数
Method applyMethod = GenericTest.class.getMethod("applyVector",Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
System.out.println(pType.getRawType());
System.out.println(pType.getActualTypeArguments()[0]);
}
44.类加载器及及委托机制的深入分析
当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当线程的类加载器去加载线程中的第一个类.
如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B.
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类.
每个类加载器加载类时, 又先委托给其上级类加载器.
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassloader的原因.
public class ClassLoaderTest {
/**
* @param args
*/
public static void main(String[] args) {
System.out.println(ClassLoader.class.getClassLoader().getClass().getName()
);
System.out.println(
System.class.getClassLoader()
);
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
45.自定义类加载器的编写原理分析
编写自己的类加载器
知识讲解:
自定义的类加载器的必须继承ClassLoader
loadClass方法与findClass方法
defineClass方法
编程步骤:
编写一个对文件内同进行简单的加密的程序.
编写了一个自己的类加载器,可实现对加密过的类进行装载和解密.
编写一个程序调用类加载器加载类,在源程序中不能用该类名定义以引用变量,因为编译器无法识别这个类.程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下类加载器或者系统类加载器,然后再使用Class.forName.
实验步骤:
对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如:javaMyclassLoader MyTest.class F:/itcast
运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLodader:java MyClassLoader MyTest F:/itcast
用加密后的类文件替换ClassLoader类装载器装载失败.
删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了.
46.编写对class文件进行加密的工具类
package src05;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader {
private static final int Oxff = 0;
/**
* @param args
*/
public static void main(String[] args) throws Exception {
String srcPath = args[0];
String destPath = args[1];
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cypher(InputStream ips,OutputStream ops)throws Exception{
int b = -1;
while((b = ips.read())!=-1){
ops.write(b^Oxff);
}
}
}
47.编写和测试自己编写的解密类加载器
protected Class<?> findClass(String name) throws ClassNotFoundException{
FileInputStream classFileName = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new Byte ArrayOutputStream();
cypher(fis,bos);
fis.close();
byte[] bytes = bos.toByteArray();
return defineClass(bytes,0,bytes.length);
}catch(Exception e){
e.printStackTrace();
}
return super.findClass(name);
最后重启MyEclipse
48类加载器的一个高级问题的实验分析
编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后, 看到打印结果为WebAppClassloader.
把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader.