黑马程序员 泛型、类加载器

本文详细介绍了Java泛型的基本概念及其在集合中的应用,包括泛型类型的定义、泛型限定及类加载器的工作原理。通过示例展示了如何使用泛型限制集合元素类型,同时解释了泛型在编译阶段的作用以及类加载器的不同类型和加载顺序。

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

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

泛型是jdk1.5中一个非常的重要的新特性,它涉及的内容很多,其中有高级算法之类,需要很高的数学技巧才能掌握。不过我们用到的基本都是常用的地方,不需要掌握那么高级的内容,只需要知道泛型在一般的程序中有哪些常见的应用即可。


最常见的泛型就是用在集合上了,如果使用集合的时候,我们不加泛型,编译器会给出安全提示。虽然仍然可以通过编译,但是用泛型更方便,之后就不需要强制转化了。下面就用ArrayList集合来做说明:


ArrayList<E>类定义和ArrayList<Integer>类引用中的术语:

ArrayList<E> -----> 泛型类型

E --------> 类型变量或类型参数

ArrayList<Integer> -----> 参数化的类型

Integer --------> 类型参数的实例或实际类型参数

<> -------------> typeof

ArrayList -------->原始类型


泛型限定:

<>中写入的类型即已经限定了可以使用的类型,它不仅仅用于集合上,还可以用在方法上、类上等,如:

public <T> void show(T t){……}

即可对接收的参数进行类型限定


public <A extends Collecton>A getCollection(Class<T> collection){……}

这里返回值是A类型,那么A是什么呢?用泛型来说明A就是Collection的子类,这样就方便使用许多了。


当泛型定义类上的时候,就是在类中所有的方法上使用了该泛型,如:

class Demo<T>{……}

定义后所有方法均视为对T使用,如:Demo<T> d = new Demo<T>();


泛型的作用很广,这了结合反射写出一个例子:

package cn.itcast.day2;

import java.util.ArrayList;

public class GenericDemo {

	public static void main(String[] args) throws Exception {
		//定义一个用Integer泛型限定的集合
		ArrayList<Integer> al1 = new ArrayList<Integer>();
		al1.add(123);
//		al1.add("abc");    不能加入String类型,因为对al做了泛型限定,只能加入Integer类型
		
		//定义一个用String泛型限定的集合
		ArrayList<String> al2 = new ArrayList<String>();
		al2.add("sss");
//		al2.add(111);    不能加入Integer类型,因为对al做了泛型限定,只能加入String类型
		
		//比较al1与al2的Class类是否相同,结果发现是相同的,那么说明泛型只是作用在编译时
		System.out.println(al1.getClass()==al2.getClass());
		
		//既然泛型只作用于编译时,那么用反射穿过编译器,直接往ArrayList类中加入泛型限定外的类型会何如呢
		//通过反射往al1中加入String类型 “gg”
		al1.getClass().getMethod("add", Object.class).invoke(al1, "gg");
		System.out.println(al1.get(1));
		//通过编译,并打印出gg
	}

}
这个例子很好的说明了泛型的作用,可以限制不是所限定的类型加入集合,但是只在编译时作用。当用到反射的方式调用add方法时就能通过编译,这更能帮助我们理解泛型的作用时机。


类的加载器:

当class文件从硬盘写入到内存是,需要将硬盘上的二进制数据转成字节码文件的对象,在加载到内存中,这就需要用到类加载器了。类加载器的作用就是将class文件加载到内存中,以实现对其的调用。这是Java中一种特有的机制,所有的类都是通过加载器加载进内存中的,这种方式保证了安全性。


类加载器同时也是一种类,那么它也需要被类加载器加载,这么一直推算下来,总一个有所加载器的父加载器,那么它是如何加载进内存中的呢?这个类就是BootStrap类,它是用C++写的一段程序,不需要使用Java中的类加载器机制,存在于jvm中,当jvm启动的时候,就会自动加载。


用代码验证类加载器机制:

package cn.itcast.day2;

public class LoadClassDemo1 {
	public static void main(String[] args) {
		//得到这个类的类加载器
		ClassLoader loader = LoadClassDemo1.class.getClassLoader();
		//如果父类不为空,就打印父类加载器的名字
		while (loader != null) {
			System.out.println(loader.getClass().getName());
			loader = loader.getParent();
		}
		//打印最父类的加载器
		System.out.println(loader);
	}
}
打印结果为:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
可以看出来,加载器一共有三个,分别是:

AppClassLoader,ExtClassLoader,BootStrap(用C++写的,打印为null)

当我们使用任何一个已有类的时候,都是使用这3个加载器加载的,那么这3个加载器到底分别负责哪些类的加载呢?它们的加载顺序是怎样的呢?

先想一个问题,如果我们写一个String类,然后调用这个类,到底是使用jdk中的String类还是我们自己写的String类呢,这就涉及到了类加载器的问题。很显然,会使用jdk中的String类,这便是加载器的委托机制。


这就是3个不同加载器的所加载的内容,BootStrap负责rt.jar包中的东西,ExtClassLoader负责ext文件间下的所有jar包,而AppClassLoader负责classpath指定的有所jar包或者目录。
例子中的LoadClassDemo1就是由AppClassLoader加载器加载的,当我们自己写一个String类时,是不会被类加载器加载的,因为有个委托机制。
当需要加载一个类的时候,会先寻找其父类加载器,一直从上往下的加载,当找到同名的类时即加载完毕,不会再加载自己写的类。

自己写一个类加载器:
我们可以自己写一个类加载器,jdk中提供了这样一个类ClassLoader,只要extends这个类,就能很快键的写出自己的类加载器,这里有2个主要的方法:
1. loadClass()
2. findClass()
 loadClass()负责实现委托机制,不断寻找其父类加载器;
当父类加载器中找不到该类时,执行findClass()中的加载方法。
所以,我们写一个类加载器时,只要覆盖其中的findClass()方法即可,便很容易实现我们自己的类加载器。
class NetworkClassLoader extends ClassLoader {
    String host;
    int port;

    public Class findClass(String name) {
    	//接收class文件的字节数组,返回类加载器
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }

    private byte[] loadClassData(String name) {
        // 获取需要加载的class文件的byte数组
    	…
    }
}
这就是一个最简单的类加载器,如果需要,我们可以给类加载器加上密钥,只要当使用我们自己的类加载器才能将此类加载进内存。






---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值