看完后让你成为武松,手把手教你打死Java中的纸老虎
时间:2025-11-04 00:08:42 出处:域名阅读(143)
泛型,看完其实算是后让虎Java当中比较难的语法了,很多人一开始都对其一知半解,武松也很害怕阅读带泛型的手把手教源码,虽然看起来语法很难,打死但当你理解后会觉得很简单,中的纸老其实只是看完一个纸老虎罢了。下面,后让虎我将会用非常简单易懂的武松方式带你去理解它,相信你在认真看完后会有非常大的手把手教收获,从此不会再畏惧它!

一. 泛型的打死定义
这里大家可以不必去看网上的有些定义,因为相对于比较学术化,中的纸老只需记住泛型可以在程序设计中指定某种类型,看完让程序的后让虎设计更加规范化即可
二. 为什么要用到泛型
了解到了泛型是什么后,那我们来讨论讨论为什么要用泛型这个语法,武松这个语法到底是干什么的?别急,下面,我先给大家举一个例子:
class Stack { public Object[] objects; public int top; public Stack() { this.objects =new Object[10]; } public void push(Object obj) { objects[this.top++] = obj; } public Object get() { return objects[this.top-1]; } }大家可以看看这是在干什么呢?这是我们自己写了一个栈,b2b信息网然后将栈里的数组类型设置成Object类型,这样的话这个栈里任意类型的数据都可以存放了(Object类是任何类的父类,不管插入什么类型的数据,都可以发生向上转型)
下面,我们来测试一下:
public class Test { public static void main(String[] args) { Stack stack=new Stack(); stack.push(1); stack.push(2); stack.push("123"); String str=(String)stack.get(); } }可以看到,我们可以向自己写的栈里放入整形以及字符串等等任何类型的数据,但注意一下取出数据的时候要进行强制类型转换
以上这样写,可以向栈里存放任何类型的数据,比较通用,其优点也可以变成缺点,正因为太通用了,使代码的规范性降低,看起来比较凌乱,这时候,我们可以考虑使用泛型,这样可以在类中或者Java集合中存放特定的数据(使用Java集合时,一般都要用到泛型,而自定义的类型中可以使用泛型也可以不使用)
三. 泛型的写法
以自定义的类型为例,云服务器提供商写法为在类名后面加上尖括号,里面写上一个字母(注意,此处写任何字母都可以,只起到一个标记这个类为泛型类的作用)
class Stack而在new对象时,以栈里只能存放整形为例,前面的尖括号必须写基本数据类型对应的包装类,而后面的尖括号可以不用写,示例如下:
Stack stack = new Stack<>();补一下Java中的基本数据类型与对应的包装类:

因此,我们前面写的自定义的栈可以写成以下形式(以存放整形为例):
class Stack<T> { public T[] objects; public int top; public Stack() { this.objects = (T[])new Object[10]; } public void push(T obj) { objects[this.top++] = obj; } public T get() { return objects[this.top-1]; } } Stack<Integer> stack = new Stack<>(); stack.push(1); stack.push(2); int ret = stack.get(); System.out.println(ret);特别注意此处:public Stack() { this.objects = (T[])new Object[10]; }
这里不能写成this.objects=new T[10];
原因:
不能new泛型类型的数组 也可理解为泛型是先检查后编译的,如果new泛型类型的数组的话,编译器检查时并不知道T是什么类型的,因此会报错。而编译的时候才会进行擦除机制,都会将其转换为Object类型 正因为有这个擦除机制,这里才能进行数组整体强制类型转换(一般数组不能整体进行强制类型转换),因为泛型只是在编译的云南idc服务商时候起作用,而实际运行时都会被擦除成Object类型,即实际运行时是没有泛型这个概念的,也即实际运行时类型都是一样的,所以T本质上是object类型的,所以此代码等价于不进行强制类型转换!!! 而直接指定泛型的代码(不是T) 比如:Stack和Stack都是在运行时直接把尖括号里的类型擦掉了,可以看到直接打印的结果(并没有打印出类型):

此处注意多理解理解

四. 泛型的使用实例
1. 求最大值
以上就是泛型的一个重要知识点了,但光看是不够的,还是得通过例子让大家有一个更为深入的理解,比如,如何写一个泛型类来求数组的最大值呢?
基本的框架大概是这样的:(没看懂的小可爱好好看看上面讲的内容哦)
class Algorithm<T extends Comparable<T>> { public T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { if(max < array[i]) { max = array[i]; } } return max; } }但是此代码if(max < array[i])会报错,为什么呢?因为将来给T传的值一定是一个引用类型,引用类型不能直接比较大于或者小于的,是要用Comparable或Comparator接口里的方法比较的,因为泛型在编译的时候会被擦除成Object类型,但Object类本身并没有实现Comparable或Comparator接口,所以我们要控制其不要擦除到Object类,所以要给泛型指定一个边界
具体写法如下:
class Algorithm<T extends Comparable<T>> { public T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { //max < array[i] if(max.compareTo(array[i]) < 0) { max = array[i]; } } return max; } } class Algorithm<T extends Comparable<T>>注意,extends叫做上界,此代码代表的意思为T这个泛型类会擦除到实现了Comparable接口的地方,换句话说,这个T类型一定是实现了Comparable接口的
同理:这个代码public class MyArrayList { ... }代表E为Number的子类或Number本身
下面让我们来用一下:
Algorithm<Integer> algorithm1 = new Algorithm<>(); Integer[] integers = {1,2,13,4,5}; Integer ret = algorithm1.findMax(integers); System.out.println(ret);运行结果如下:

成功了!
2. 优化
经过上面的努力,我们已经写出了一个泛型类来求一个数组的最大值了,但是,上面的例子是一个整形数组,那么我们能不能在数组里存放别的类型去比较呢?答案是可以的,但是我们还得去new一个对象,例如:Algorithm algorithm2 = new Algorithm<>();这样很麻烦。但是我们可以将求最大值的方法设置成静态的class Algorithm2 ,因为是静态的方法,不需要new对象,所以就没有在new对象时指定泛型的过程了,所以没必要给方法后加尖括号,但是去掉之后,代码又会被错:

我们可以这样修改:
class Algorithm2 { public static<T extends Comparable<T>> T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { if(max.compareTo(array[i]) < 0) { max = array[i]; } } return max; } }此方法public static> T findMax(T[] array){}叫做泛型方法
下面继续带大家来用一下:
public static void main(String[] args) { Integer[] integers = {1,2,13,4,5}; //会根据形参的类型推导出整个泛型的类型参数 Integer ret = Algorithm2.findMax(integers); System.out.println(ret); Integer ret2 = Algorithm2.<Integer>findMax(integers); System.out.println(ret2); }注意,ret1写法和ret2写法是一样的,都可以
打印结果如下:

五. 通配符
1. 基本写法
通配符也是泛型的一种,下面我们来写一个泛型方法来打印集合中的元素。
class Test { public static<T> void print(ArrayList<T> list) { for (T t : list) { System.out.println(t); } }这个写法很简单,上文都讲过了,那么让我们来试着用一下吧:
public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); Test.print(list); }打印的结果如下:

除了以上这种写法,我们还可以将其改成通配符的写法,先给大家上代码:
//?代表通配符 擦除机制 Object public static void print2(ArrayList<?> list) { for (Object t : list) { System.out.println(t); } } }此处for (Object t : list)必须这样写,因为通配符也是有擦除机制的,会在编译器编程Object类型。
2. 上界
语法: 示例: public static void printAll(MyArrayList list) { ... } xxxxxxxxxxbr public static void printAll(MyArrayList list) {br...br }代表可以传入类型实参是 Number 子类的任意类型的 MyArrayList所以以下调用都是正确的:
printAll(new MyArrayList()); printAll(new MyArrayList()); printAll(new MyArrayList()); xxxxxxxxxxbr printAll(new MyArrayList());brprintAll(new MyArrayList());brprintAll(new MyArrayList());以下调用都是错误的:
printAll(new MyArrayList()); printAll(new MyArrayList ()); xxxxxxxxxxbr printAll(new MyArrayList());brprintAll(new MyArrayList());3. 下界
下界和上界的用法很类似
语法: 示例: public static void printAll(MyArrayList list) { ... } xxxxxxxxxxbr public static void printAll(MyArrayList list) {br...br}代表可以传入类型实参是 Integer 父类的任意类型的 MyArrayList所以以下调用是正确的:
printAll(new MyArrayList()); printAll(new MyArrayList()); printAll(new MyArrayList()); xxxxxxxxxxbr printAll(new MyArrayList());brprintAll(new MyArrayList());brprintAll(new MyArrayList());以下调用是错误的:
printAll(new MyArrayList()); printAll(new MyArrayList()); xxxxxxxxxxbr printAll(new MyArrayList());brprintAll(new MyArrayList());六. 泛型的限制
学习完后,我们应该注意泛型使用过程中以下一些限制:
泛型类型参数不支持基本数据类型
无法实例化泛型类型的对象
无法使用泛型类型声明静态的属性
无法使用 instanceof 判断带类型参数的泛型类型(因为被擦除机制擦除了)
无法创建泛型类数组
无法 create、catch、throw 一个泛型类异常(异常不支持泛型)
泛型类型不是形参一部分,无法重载
好啦,本次泛型知识点的分享就先告一段落了,整理不易,但如果能帮到大家很开心了。也希望大家多理解理解,不论是刚开始学习还是复习,都值得仔细揣摩哦!一起加油吧!
猜你喜欢
- 通过激情探索学习(以passion为引导,最大化教程的效果与体验)
- 在http://blog.csdn.net/dctfjy/article/details/4233284中找到了解决办法: 复制代码代码如下: 重启之后,网络正常了。 复制代码代码如下: 重启网卡:/etc/init.d/networking restart 这样设置完成后,虚拟机就既能和主机通信,又能连接到Internet了。其中,虚拟机和主机通信是由其IP决定的,IP和主机的虚拟网卡 Vmware Network Adapter VMnet8的IP一致,连接到Internet则是利用了Vmware提供的NAT服务为虚拟交换机VMnet8作路由,也就是网关设置为虚拟NAT服务器的地址。这里,由于用了静态IP,所以没有用到Vmware的DHCP服务。 其实说了这么多,重点无非就是要获取网关等信息而已,知道了这些,和其他的静态IP设置没有区别。
- 有些Linux用户在使用了Ubuntu系统后,不知道该怎么安装Visual Studio Code,今天小编就帮你解决这个问题吧,下面就是具体的安装步骤了。安装方法:安装过程中,将会询问安装路径,如下图:经过一系列要求和条件后,询问你是否确认安装Visual Studio Code。输入“a”来确定:确定之后,安装程序会开始下载并安装。安装完成后,可以发现Visual Studio Code图标已经出现在Unity启动器上。下图是Ubuntu 15.04 Unity的截图:卸载Visual Studio Code,同样使用Ubuntu Make命令。如下:umake web visual-studio-code --remove假如不使用Ubuntu Make,也可以通过微软官方下载安装文件。Download Visual Studio Code for Linux以上就是在Ubuntu系统中安装Visual Studio Code的方法了,还不会安装Visual Studio Code的用户,就参考上面的步骤进行安装吧。
- 电脑致命错误(解决电脑致命错误的关键步骤及常见问题解析)
- 教你简易转换安卓手机m4a音频为mp3格式(一键操作,高效转换,享受更广泛的音频播放体验)
- 想知道怎样从Ubuntu 14.04 LTS 升级到Ubuntu 14.10么? 这就是大家要讲的Canonical不会强迫14.04的用户升级到14.10这个中间版本但这并不意味着你不能将你的坚如磐石的Trusty Tahr升级到(有点让人印象深刻的)Utopic Unicorn。要得到非LTS版本的Ubuntu发布通知,你需要在软件和更新工具中选择。这个很直接。打开 ‘软件和更新’选择 ‘更新’ 选项进入‘有新版本Ubuntu时通知我’的选项在下拉菜单选项中将‘对于长期支持版本’改成‘对于任何版本’切换后你会想快点更新。现在准备就绪!Canonical此刻会弹出“升级”提示给用户(相比较普通的ISO镜像而言经常延迟)可以通过软件更新工具更新到14.10了。
- ubuntu无法解析亚马逊的DNS解决方法 编辑/etc/resolv.conf文件。 将 nameserver改为 8.8.8.8 或者8.8.4.4 再重启网络就解决问题。 /etc/init.d/networking restart
- ubuntu xrandr修改分辨率 $ cvt 1024 768 # 1024×768 59.92 Hz (CVT 0.79M3) hsync: 47.82 kHz; pclk: 63.50 MHz Modeline “1024x768_60.00″ 63.50 1024 1072 1176 1328 768 771 775 798 -hsync +vsync 运行下边的命令将改变您显示器的分辨率,但是效果是临时的 $ xrandr --newmode “1024x768_60.00″ 63.50 1024 1072 1176 1328 768 771 775 798 -hsync +vsyn $ xrandr --addmode VGA1 1024x768_60.00 $ xrandr --output VGA1 --mode 1024x768_60.00
- 电脑表格显示格式错误及解决方法(解析电脑表格显示错误的常见问题和解决方案)