41、自定义泛型方法的练习与类型推断总结:
(6)类型参数的类型推断:太复杂不常用-->省略
42、自定义泛型类的应用:
//定义测试用类:
public class Person {}
如果我们采用如上的方式定义方法,那么我们在调用的时候会出现如下情况:
public static void main(String[] args) {
GenericDao ge = new GenericDao();
ge.add(new Person());//我们添加的是Person类型
String s = ge.find(1);//而我们却可以以String类型的方式返回,编译器并不报错
}
但是我们在add的时候以T的类型传递过去的,返回的时候也是以T的类型返回的,那么为什么不行呢,因为这是两个方法,
你可以认为两个方法定义的T毫无关系。那么如何把T应用到这个类中的整个方法呢。我们应该把T定义到类名上就可以了
代码示例:
如果这样定义,我们就能保证添加进去的和拿出来的是同一类的对象。
注意:当一个变量或方法被声明为泛型的时候,只能被实例变量和方法调用,而不能被静态变量
和静态方法调用。因为静态成员变量是被所有参数化的类的对象所共享的。如下:
43、通过反射获得泛型实际类型参数:
示例代码:
对于Vector<Date> v1 如果只是根据v1这个对象,你是无法拿到Vector的参数化类型Date的,但是当这个类型的对象作为一个方法的参数的
时候,我们就可以利用反射的方式得到他了。
44、类加载器及其委托机制的深入分析
类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象
当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:
类加载器的树形结构
BootStrap 加载:JRE/lib/rt.jar
| |
| |
ExtClassLoader 加载:JRE/lib/ext/*.jar
| |
| |
AppClassLoader 加载:classpath指定的所有jar或目录
在AppClassLoader下面我们可以把我们自己写的ClassLoader挂载到下面用来装载我们的放在
特定目录的类。
(一、)当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
(1)首先当前线程的类加载器去加载线程中的第一个类。
(2)如果类A中引用了类B,Java虚拟机降使用加载类A的加载器来加载类B
(3)还可以直接调用ClassLoadedr.loadClass()方法来指定某个类加载器去加载某个类
(二、)每个类加载器加载文件的时候,都是先委托给其上级类加载器
(1)当所有的祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了
则抛出ClassNotFoundException,不是在去找发起者类加载器的儿子,因为没有
getChild方法,即使有,那有多个儿子找那一个呢?
(2)对类加载器的层次图和委托架子啊原理,解释先前将ClassLoaderTest输出成jre/lib/ext
目录下面的itcast.jar包后,运行结果为ExtClassLoader的原因?双亲委派机制
面试题:能不能自己写个类叫java.lang.String让泪加载器去加载他?
不能:为了不让我们写String类,类加载器采用委托机制,总是先让他的父类加载器
加载,这样总会加载到rt.jar所以总是使用java系统提供的String
示例代码:
45、自定义类的加载器的编写原理分析:
ClassLoader是一个抽象类,里面有两个我们要写自己的类加载器将要用到的方法
//通过给他一个类名,来加载一个类返回这个类的字节码,这个方法内部在调用这个方法的时候会先
//找他的父加载器去加载,然会回来在去找findClass(String name)方法,如果你把这个方法覆盖了它加载的
//时候不会去找父类了,我们还要自己去写这段代码,所以我们用覆盖findClass(String name)方法就行了(保存流程)
(1)public Class<?> loadClass(String name):
//把一份字节数组转化为一个Class的一个实例对象
(2)protected final Class<?> defineClass(String name,byte[] b,int off, int len);
概念:模板方法的设计模式:
父类
子类1 子类2
许多相同的部分都放到父类中,而具体的细节由我们的子类自己完成自己的那部分,所以
重写父类的方法加入我们自己实现的细节就可以了。
46、编写对class文件进行加密的工具类
(1)编写加密工具类
Run as --->args参数:D:\J2SE\EclipseWorkSpace\javaenhance\bin\cn\itcast\day2\ClassLoaderAttachment.class itcast
第一个参数是你写的那个要加密的类生成的.class文件的路径
第二个参数是加密完成之后,生成的文件放到哪个文件夹下面
结果:在我们对ClassLoaderAttachment.toString()进行打印的时候,会输出hello world 因为这个时候他的.class文件没有加密,当我们用家完密
的.class文件覆盖它的.class文件 然后再运行就出错了。
47、编写和测试自己编写的解密类加载器
在上一个示例中,我们对类的.class文件进行了加密,所以无法运行,在这个例子中我们进行解密操作;
在MyClassLoader 继承 ClassLoader并重写他的findClass(String name) 方法:
//添加二个构造方法和一个属性用于传递.class文件名
- (1)编写一个方法自动将Object类型转化为其他的类型
- 代码示例:
- public static <T>T autoConvert(Object obj) {
- return (T)obj;
- }
- (2)将任意类型数组中的每一个元素填充为任意类型的对象。
- 代码示例:
- public static <T>void fillArray(T[] a, T obj) {
- for(int i=0; i<a.length; i++) {
- a[i] = obj;
- }
- }
- (3)采用自定义泛型的方法打印出任意参数化集合类型中的内容:
- 代码示例:
- //采用通配符
- public static void printCollection(Collection<?> coll) {
- for(Object obj : coll) {
- System.out.println(obj);
- }
- //错误:因为无法知道?到底是什么类型,你add一个String类型,万一?是Integer呢?
- //coll.add("abc");
- }
- //采用泛型
- public static <T>void printCollection2(Collection<T> coll, T obj2) {
- for(Object obj : coll) {
- System.out.println(obj);
- }
- coll.add(obj2);
- }
- (4)把任意参数化类型的数组中的数据安全的复制到相应类型的集合中
- 代码示例:
- public static <T>void copyCollection1(Collection<T> dist, T[] src) {
- for(int i=0; i<src.length; i++) {
- dist.add(src[i]);
- }
- }
- (5)把任意参数类型的数组中的数据安全的复制到另外一个数组中:
- 代码示例:
- public static <T>void copyCollection2(T[] dist, T[] src) {
- }
- 调用:
- public static void main(String[] args) {
- //T表示的都是String
- copyCollection1(new Vector<String>(), new String[20]);
- //取交集:Object
- copyCollection2(new Date[10], new String[10]);
- //错误:new Vector<Date>已经指定了T:Date 而后面又new String:这是不可以的
- //这叫做泛型的传播性
- //copyCollection1(new Vector<Date>(), new String[20]);
- }
(6)类型参数的类型推断:太复杂不常用-->省略
42、自定义泛型类的应用:
- 代码示例:
- /**
- * 自定义泛型模仿Dao层的crud
- * @author Administrator
- *
- */
- public class GenericDao {
- public <T>void add(T obj) {
- }
- public <T>T find(int id) {
- return null;
- }
- }
public class Person {}
如果我们采用如上的方式定义方法,那么我们在调用的时候会出现如下情况:
public static void main(String[] args) {
GenericDao ge = new GenericDao();
ge.add(new Person());//我们添加的是Person类型
String s = ge.find(1);//而我们却可以以String类型的方式返回,编译器并不报错
}
但是我们在add的时候以T的类型传递过去的,返回的时候也是以T的类型返回的,那么为什么不行呢,因为这是两个方法,
你可以认为两个方法定义的T毫无关系。那么如何把T应用到这个类中的整个方法呢。我们应该把T定义到类名上就可以了
代码示例:
- public class GenericDao<T> {
- public void add(T obj) {
- }
- public T find(int id) {
- return null;
- }
- }
注意:当一个变量或方法被声明为泛型的时候,只能被实例变量和方法调用,而不能被静态变量
和静态方法调用。因为静态成员变量是被所有参数化的类的对象所共享的。如下:
- public class GenericDao<T> {
- //报错:
- public static void update(T obj) { }
- 应改为:
- public static void update(T obj) { }
- }
示例代码:
- public static void main(String[] args) {
- //得到方法applyVector的反射对象
- Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
- //得到这个方法的"泛型的参数化类型"返回一个数组,因为这个方法可能有多个参数
- Type[] types = applyMethod.getGenericParameterTypes();
- //因为我们知道我们的方法中只有一个参数所以:types[0]
- ParameterizedType pType =(ParameterizedType) types[0];
- //得到第一参数的真实类型:Vector
- System.out.println(pType.getRawType());
- //.getActualTypeArguments():得到第一个参数的参数化类型(可以简单理解为:就是泛型),是一个数组,因为可能有多个例如:Map<K,V>
- System.out.println(pType.getActualTypeArguments()[0]);
- }
- public static void applyVector(Vector<Date> v1) {
- }
对于Vector<Date> v1 如果只是根据v1这个对象,你是无法拿到Vector的参数化类型Date的,但是当这个类型的对象作为一个方法的参数的
时候,我们就可以利用反射的方式得到他了。
44、类加载器及其委托机制的深入分析
类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象
当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:
类加载器的树形结构
![](http://dl.iteye.com/upload/attachment/222706/8ac9f9dc-f8c7-3feb-ad3f-ee52eee1c8d3.jpg)
BootStrap 加载:JRE/lib/rt.jar
| |
| |
ExtClassLoader 加载:JRE/lib/ext/*.jar
| |
| |
AppClassLoader 加载:classpath指定的所有jar或目录
在AppClassLoader下面我们可以把我们自己写的ClassLoader挂载到下面用来装载我们的放在
特定目录的类。
(一、)当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
(1)首先当前线程的类加载器去加载线程中的第一个类。
(2)如果类A中引用了类B,Java虚拟机降使用加载类A的加载器来加载类B
(3)还可以直接调用ClassLoadedr.loadClass()方法来指定某个类加载器去加载某个类
(二、)每个类加载器加载文件的时候,都是先委托给其上级类加载器
(1)当所有的祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了
则抛出ClassNotFoundException,不是在去找发起者类加载器的儿子,因为没有
getChild方法,即使有,那有多个儿子找那一个呢?
(2)对类加载器的层次图和委托架子啊原理,解释先前将ClassLoaderTest输出成jre/lib/ext
目录下面的itcast.jar包后,运行结果为ExtClassLoader的原因?双亲委派机制
面试题:能不能自己写个类叫java.lang.String让泪加载器去加载他?
不能:为了不让我们写String类,类加载器采用委托机制,总是先让他的父类加载器
加载,这样总会加载到rt.jar所以总是使用java系统提供的String
示例代码:
- public class ClassLoaderTest {
- public static void main(String[] args) {
- //AppClassLoader
- System.out.println(
- ClassLoaderTest.class.getClassLoader().getClass()
- .getName()
- );
- //报错:getClassLoader()拿到的是null因为System类由BootStrap类加载器加载,而这个加载器我们是拿不到的
- //是嵌在JVM中的,
- System.out.println(
- System.class.getClassLoader().getClass()
- .getName()
- );
- ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
- //输出所有的类加载器
- while(classLoader != null) {
- System.out.println(classLoader);
- //拿到它的父类加载器
- classLoader = classLoader.getParent();
- }
- System.out.println(classLoader);
- //输入结果:AppClassLoader、ExtClassLoader、null null是因为我们拿不到了,实际上是BootStrap
- }
- }
ClassLoader是一个抽象类,里面有两个我们要写自己的类加载器将要用到的方法
//通过给他一个类名,来加载一个类返回这个类的字节码,这个方法内部在调用这个方法的时候会先
//找他的父加载器去加载,然会回来在去找findClass(String name)方法,如果你把这个方法覆盖了它加载的
//时候不会去找父类了,我们还要自己去写这段代码,所以我们用覆盖findClass(String name)方法就行了(保存流程)
(1)public Class<?> loadClass(String name):
//把一份字节数组转化为一个Class的一个实例对象
(2)protected final Class<?> defineClass(String name,byte[] b,int off, int len);
概念:模板方法的设计模式:
父类
子类1 子类2
许多相同的部分都放到父类中,而具体的细节由我们的子类自己完成自己的那部分,所以
重写父类的方法加入我们自己实现的细节就可以了。
46、编写对class文件进行加密的工具类
(1)编写加密工具类
- public class MyClassLoader {
- public static void main(String[] args) throws Exception {
- String srcPath = args[0]; //D:\J2SE\EclipseWorkSpace\javaenhance\bin\cn\itcast\day2\ClassLoaderAttachment.class
- String destDir = args[1]; //itcast
- String destFileName = srcPath.substring(srcPath.lastIndexOf('\\') + 1); //ClassLoaderAttachment.class
- String destPath = destDir + "\\" + destFileName; //itcast\ClassLoaderAttachment.class
- FileInputStream fis = new FileInputStream(srcPath);
- FileOutputStream fos = new FileOutputStream(destPath);
- cyper(fis, fos );
- }
- public static void cyper(InputStream ips, OutputStream ops) throws Exception {
- int b = -1;
- while((b =ips.read())!= -1) {
- ops.write(b ^ 0xff);
- }
- }
- }
- (2)生成.class文件的测试类
- public class ClassLoaderAttachment extends Date {
- @Override
- public String toString() {
- return "hello world";
- }
- }
- (3)运行测试
- //出错:
- System.out.println(new ClassLoaderAttachment().toString());
第一个参数是你写的那个要加密的类生成的.class文件的路径
第二个参数是加密完成之后,生成的文件放到哪个文件夹下面
结果:在我们对ClassLoaderAttachment.toString()进行打印的时候,会输出hello world 因为这个时候他的.class文件没有加密,当我们用家完密
的.class文件覆盖它的.class文件 然后再运行就出错了。
47、编写和测试自己编写的解密类加载器
在上一个示例中,我们对类的.class文件进行了加密,所以无法运行,在这个例子中我们进行解密操作;
在MyClassLoader 继承 ClassLoader并重写他的findClass(String name) 方法:
//添加二个构造方法和一个属性用于传递.class文件名
- public MyClassLoader() {
- }
- private String fileDir;
- public String getFileDir() {
- return fileDir;
- }
- public void setFileDir(String fileDir) {
- this.fileDir = fileDir;
- }
- public MyClassLoader(String fileDir) {
- this.fileDir = fileDir;
- }
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- //找到那个错误的.class文件的位置
- String filePath = fileDir + '\\' + name + ".class";
- FileInputStream fis;
- try {
- fis =new FileInputStream(filePath);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- //解密
- cyper(fis, baos);
- fis.close();
- byte[] bytes = baos.toByteArray();
- baos.close();
- //解密完成之后调用defineClass方法返回一个Class对象
- Class clazz = defineClass(bytes, 0, bytes.length);
- return clazz;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return super.findClass(name);
- }
- 测试运行:
- Class clazz = new MyClassLoader("itcastlib").findClass("ClassLoaderAttachment");
- //这个地方不呢个直接写: ClassLoaderAttachment d1 = (ClassLoaderAttachment)clazz.newInstance();
- //因为如果这样写,因为用到了ClassLoaderAttachment这个类ClassLoader会又去加载这个类的字节码这个时候还没有解密所以会报错
- //所以先写他的父类
- Date d1 = (Date)clazz.newInstance();
- System.out.println(d1.toString());