当前位置: 首页 > 编程日记 > 正文

Java学习笔记(二一)——Java 泛型

【前面的话】

     最近脸好干,掉皮,需要买点化妆品了。

     Java泛型好好学习一下。

【定义】

一、泛型的定义主要有以下两种:

  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

        不论使用那个定义,泛型的参数在真正使用泛型时都必须作出指明。

二、使用泛型的目的:

  1. 一些强类型程序语言支持泛型,其主要目的是加强类型安全及减少类转换的次数,但一些支持泛型的程序语言只能达到部份目的。
  2. 泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象所重用。
  3. 是对java语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值得占位符一样。

Java泛型的几种类型代码】

一、不使用泛型的代码

     我们定义一个Person类,包含三个属性x,y,z。在我们开始定义地时候,我们也不知道这三个属性是用来干什么的,所以我们定义为Object类型。但是在使用的时候,我们分别对x,y,z赋予了int,double,String类型,所以在取出的时候,我们需要把这三个类型值进行强制转换。如下代码:

    1. Person.java

 1 public class Person {
 2     private Object x;
 3     private Object y;
 4     private Object z;
 5     //使用Object类型。可以转化为任何类型
 6     public Object getX() {
 7         return x;
 8     }
 9     public void setX(Object x) {
10         this.x = x;
11     }
12     public Object getY() {
13         return y;
14     }
15     public void setY(Object y) {
16         this.y = y;
17     }
18     public Object getZ() {
19         return z;
20     }
21     public void setZ(Object z) {
22         this.z = z;
23     }
24 }

    2. NoGenericTest.java

 1 public class NoGenericTest {
 2     public static void main(String[]args){
 3         Person boy=new Person();
 4         boy.setX(20);
 5         boy.setY(22.2);
 6         boy.setZ("帅哥TT");
 7         //这里根据设置的不同类型的值,我们需要进行强制类型转化。
 8         int x=(Integer)boy.getX();
 9         double y=(double)boy.getY();
10         String z=(String)boy.getZ();
11         
12         System.out.println(x);
13         System.out.println(y);
14         System.out.println(z);
15     }
16 }

    3. 运行结果

1 20
2 22.2
3 帅哥TT

二、使用一个类型变量泛型的代码

      我们定义一个泛型类Person,定义三个属性x,y,z,在测试类中,我们设置属性的值,并打印。

    1. Person.java

 1 public class Person<T> {
 2     private T x;
 3     private T y;
 4     private T z;
 5     public T getX() {
 6         return x;
 7     }
 8     public void setX(T x) {
 9         this.x = x;
10     }
11     public T getY() {
12         return y;
13     }
14     public void setY(T y) {
15         this.y = y;
16     }
17     public T getZ() {
18         return z;
19     }
20     public void setZ(T z) {
21         this.z = z;
22     }
23 }

    2. GenericTest.java

 1 public class GenericTest {
 2     public static void main(String[]args){
 3         Person boy=new Person();
 4         boy.setX(20);
 5         boy.setY(22.2);
 6         boy.setZ("帅哥TT");
 7         //不用进行类型转化
 8         System.out.println(boy.getX());
 9         System.out.println(boy.getY());
10         System.out.println(boy.getZ());
11     }
12 }

    3. 运行结果

1 20
2 22.2
3 帅哥TT

三、使用两个类型变量泛型的代码

     我们定义一个泛型类Person,定义两个属性x,y,使用了两种不同的类型变量,在测试类中,我们设置属性的值,并打印。

    1. Person.java

 1 public class Person<T1,T2> {
 2     private T1 x;
 3     private T2 y;
 4     public T1 getX() {
 5         return x;
 6     }
 7     public void setX(T1 x) {
 8         this.x = x;
 9     }
10     public T2 getY() {
11         return y;
12     }
13     public void setY(T2 y) {
14         this.y = y;
15     }
16 }

    2. GenericTest.java

 1 public class GenerricTest {
 2     public static void main(String[] args){
 3         Person<String,Integer> boy=new Person<String,Integer>();
 4         boy.setX("帅哥TT");
 5         boy.setY(20);
 6         System.out.println(boy.getX());
 7         System.out.println(boy.getY());
 8     }
 9 
10 }

    3. 运行结果

1 帅哥TT
2 20

四、使用泛型的继承

     我们定义一个泛型类Person,定义两个属性x,y,然后定义另一个泛型类Boy,定义属性z,Boy继承Person类,在测试类中,我们设置属性的值,并打印。

    1. Person.java

 1 public class Person<T1,T2> {
 2     private T1 x;
 3     private T2 y;
 4     public T1 getX() {
 5         return x;
 6     }
 7     public void setX(T1 x) {
 8         this.x = x;
 9     }
10     public T2 getY() {
11         return y;
12     }
13     public void setY(T2 y) {
14         this.y = y;
15     }
16 }

    2. Boy

1 public class Boy<T1,T2,T3>extends Person<T1,T2> {
2     private T3 z;
3     public T3 getZ() {
4         return z;
5     }
6     public void setZ(T3 z) {
7         this.z = z;
8     }
9 }

    3. GenericTest.java

 1 public class GenericTest {
 2     public static void main(String[] args){
 3         Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
 4         boy.setX("帅哥TT");
 5         boy.setY(20);
 6         boy.setZ(200000.22);
 7         
 8         System.out.println(boy.getX());
 9         System.out.println(boy.getY());
10         System.out.println(boy.getZ());
11     }
12 }

   4. 运行结果

1 帅哥TT
2 20
3 200000.22

五、使用泛型的接口

     我们定义一个泛型接口Person,定义两个方法,然后定义另一个泛型类Boy,实现泛型接口Person,定义属性x,y,z,在测试类中,我们设置属性的值,并打印。

    1. Person.java

1 public interface Person<T1,T2> {
2     public T1 getX();
3     public T2 getY();
4 }

    2. Boy

 1 public class Boy<T1,T2,T3>implements Person<T1,T2> {
 2     private T1 x;
 3     private T2 y;
 4     private T3 z;
 5     public T1 getX() {
 6         return x;
 7     }
 8     public void setX(T1 x) {
 9         this.x = x;
10     }
11     public T2 getY() {
12         return y;
13     }
14     public void setY(T2 y) {
15         this.y = y;
16     }
17     public T3 getZ() {
18         return z;
19     }
20     public void setZ(T3 z) {
21         this.z = z;
22     }
23 
24 }

3. GenericTest.java

 1 public class GenericTest {
 2     public static void main(String[] args){
 3         Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
 4         boy.setX("帅哥TT");
 5         boy.setY(20);
 6         boy.setZ(200000.22);
 7         System.out.println(boy.getX());
 8         System.out.println(boy.getY());
 9         System.out.println(boy.getZ());
10     }
11 }

    4. 运行结果

1 帅哥TT
2 20
3 200000.22

六、使用泛型方法

     说明一下,定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。

      定义一个普通类Person,定义一个泛型方法,如下代码:

     1. Person.java

 1 public class Person{
 2     public static<T>T getMiddle(T[]a){
 3         return a[a.length/2];
 4     }
 5     public static void main(String [] args){
 6         String[]name={"帅哥TT","帅哥TT1","帅哥TT2"};
 7         String middle=Person.<String>getMiddle(name);
 8         System.out.println(middle);
 9         
10         Integer[]num={20,22,25};
11         Integer middle1=Person.<Integer>getMiddle(num);
12         System.out.println(middle1);
13         
14         Double[]num1={20.0,22.2,25.5};
15         Double middle2=Person.<Double>getMiddle(num1);
16         System.out.println(middle2);
17     }
18 }

    2. 运行结果

1 帅哥TT1
2 22
3 22.2

七、类型变量的限定

     如下代码,我们在方法min中定义了一个变量smallest类型为T,这说明了smallest可以是任何一个类的对象,我们在下面的代码中需要使用compareTo方法, 但是我们没有办法确定我们的T中含有CompareTo方法,所以我们需要对T进行限定,在代码中我们让T继承Comparable类。如下:

1 public static<T extends Comparable>T min(T[]a)

    1. Person.java

 1 public class Person{
 2     public static<T extends Comparable>T min(T[]a){
 3         if(a==null||a.length==0){
 4             return null;
 5         }
 6         T smallest=a[0];
 7         for(int i=1;i<a.length;i++){
 8             if(smallest.compareTo(a[i])>0){
 9                 smallest=a[i];
10             }
11         }
12         return smallest;
13     }
14     public static void main(String [] args){
15         Integer[]num={20,25,30,10};
16         Integer middle=Person.<Integer>min(num);
17         System.out.println(middle);
18     }
19 }

    2. 运行结果

1 10

Java泛型理解】

一、类型擦除

     正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。

     很多泛型的奇怪特性都与这个类型擦除的存在有关,包括:

  1. 泛型类并没有自己独有的Class类对象。比如并不存在List<String>.class或是List<Integer>.class,而只有List.class。
  2. 静态变量是被泛型类的所有实例所共享的。对于声明为MyClass<T>的类,访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过new MyClass<String>还是new MyClass<Integer>创建的对象,都是共享一个静态变量。
  3. 泛型的类型参数不能用在Java异常处理的catch语句中。因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常对应的catch语句。

二、最佳实践

在使用泛型的时候可以遵循一些基本的原则,从而避免一些常见的问题。

  1. 在代码中避免泛型类和原始类型的混用。比如List<String>和List不应该共同使用。这样会产生一些编译器警告和潜在的运行时异常。当需要利用JDK 5之前开发的遗留代码,而不得不这么做时,也尽可能的隔离相关的代码。
  2. 在使用带通配符的泛型类的时候,需要明确通配符所代表的一组类型的概念。由于具体的类型是未知的,很多操作是不允许的。
  3. 泛型类最好不要同数组一块使用。你只能创建new List<?>[10]这样的数组,无法创建new List<String>[10]这样的。这限制了数组的使用能力,而且会带来很多费解的问题。因此,当需要类似数组的功能时候,使用集合类即可。
  4. 不要忽视编译器给出的警告信息。

【参考资料】

  1. Java深度历险(五)——Java泛型
  2. java核心技术

【后面的话】

     好好学习。

——TT

转载于:https://www.cnblogs.com/xt0810/p/3664280.html

相关文章:

GitHub与Git入门

一、GitHub GitHub为开发者提供Git仓库的托管服务&#xff0c;可以进行代码共享、团队协同开发&#xff0c;创建了社会化&#xff08;social coding&#xff09;编程的概念。 二、GitHub与Git的区别 开发者将源代码存入“Git”仓库&#xff0c;而GitHub则在网络上提供Git仓库…

《UML大战需求分析》阅读笔记1

通过阅读本书的序和第一章&#xff0c;让我对于UML的理解更加深刻了&#xff0c;并且懂了怎样把你UML学的更好。 作者先让我们明白什么是UML&#xff0c;大概知道了UML各个图的形态和各种用途&#xff0c;然后再详细的介绍各个图怎样使用。 UML是个非必要的建模工具&#xff0c…

裸奔的支付X聊天,你还敢用吗?

“ 一直想在社交领域突破的某付宝&#xff0c;却自始至终对社交功能如此的不用心&#xff0c;让用户的数据在网络中裸奔&#xff0c;使用户不寒而栗。”没错&#xff0c;这篇文章要说的就是BAT中A家的支付X&#xff0c;那个千方百计做社交的支付工具。如果你使用了它的聊天窗口…

MVC3项目依赖文件错误解决

MVC3的项目依赖分为两大类&#xff1a; 1、ASP.NET Web Pages 2、ASP.NET MVC 3 如果没有正确引入&#xff0c;或者项目的版本有错误会出现程序集引用错误。 在服务器上部署时&#xff0c;解决思路如下&#xff1a; 1、下载MVC3的安装包&#xff0c;然后在服务器上安装&#xf…

让自己的开源项目支持CocoaPods集成

平时我们会经常用CocoaPods集成第三方库&#xff0c;那如何使自己的代码也可以通过CocoaPods集成呢&#xff1f;只需要简单几步&#xff1a; 创建git仓库&#xff0c;把代码提交到Github或码云等在git仓库中创建.Podspec文件&#xff0c;修改里面的配置&#xff08;如代码的版…

由于客户端检测到一个协议错误 代码0x1104

重新连接N次都还是这个错误提示&#xff0c;最后再重起电脑&#xff0c;还是没用。研究了一下错误终于解决了。 首先检查远程连接端口对不对&#xff1f;Windows远程默认的连接端口是3389&#xff0c;一般大家连接时直接输入IP或域名就可以连接了。如果还要加:端口号的话&#…

使用Fiddler抓取手机HTTP流量包

“ Fiddler手机流量抓包实战。”Fiddler作为一个PC端的HTTP/HTTPS协议分析工具&#xff0c;不仅能够抓取PC上的流量&#xff0c;还能作为代理&#xff0c;抓取手机流量&#xff0c;在之前的文章《使用Fiddler进行HTTP流量分析》中介绍了Fiddler的基本用法&#xff0c;本文介绍如…

Spark Steaming 点滴

Spark Streaming 模块是对于 Spark Core 的一个扩展&#xff0c;目的是为了以高吞吐量&#xff0c;并且容错的方式处理持续性的数据流。目前 Spark Streaming 支持的外部数据源有 Flume、 Kafka、Twitter、ZeroMQ、TCP Socket 等。Discretized Stream 也叫 DStream) 是 Spark S…

CocoaPods远程私有库

上一篇&#xff08;让自己的开源项目支持CocoaPods集成&#xff09;介绍了将自己开发的框架代码发布到Cocoapods&#xff0c;全球的开发者都可以通过pod search搜索到我们的框架代码以及通过pod install进行安装。但有时候我们希望只有我们项目内部的人才可以集成和修改&#x…

大数据平台的秘密

大数据&#xff0c;这个词越来越热&#xff0c;很多人都在谈大数据&#xff0c;其实很多张口闭口大数据的人&#xff0c;或许都不知道数据是如何产生、传递、存储、运算到应用。有段时间&#xff0c;看到一些大数据文章&#xff0c;就感觉纯属凑热闹&#xff0c;小数据都没搞明…

Fiddler使用技巧:强大的数据文本编解码功能

“ 郑重推荐Fiddler工具自带的TextWizard功能。”Fiddler作为一个HTTP类协议的抓包分析工具&#xff0c;之前已介绍过抓包分析功能&#xff0c;可参考文章&#xff1a;《使用Fiddler进行HTTP流量分析》《使用Fiddler抓取手机HTTP流量》在抓包分析功能之外&#xff0c;我们一定不…

jquery validate 详解一

jQuery校验 官网地址&#xff1a;http://bassistance.de/jquery-plugins/jquery-plugin-validation 一导入js库 <script src"../js/jquery.js" type"text/javascript"></script><script src"../js/jquery.validate.js" type"…

安装VMWare tools,以及解决安装后/mnt中有hgfs但没共享文件的方法

一、首先是安装VMWare tools安装过程可参考&#xff1a;http://www.cnblogs.com/jiu0821/p/7559949.html二、解决安装VMWare tools后/mnt中有hgfs但没共享文件 前提&#xff1a;在虚拟机软件中设置了共享目录 此时在linux中进入 /mnt/hgfs 文件夹&#xff0c;但发现共享的文件没…

音乐(文件)断点下载

这篇文章介绍音乐等文件的下载&#xff0c;支持断点续传。 我们需要创建两个类 HYDownLoader&#xff1a;音乐下载的主类&#xff0c;可以进行新建下载、暂停下载、取消下载等。 HYFileTool&#xff1a;文件管理类&#xff0c;主要为HYDownLoader服务&#xff0c;可以判断文件…

php设计模式

原型模式&#xff08;prototype&#xff09; PHP设计模式之&#xff1a;原型模式 刚刚了解了原型模式&#xff0c;通过资料以及自身了解整合以下资料&#xff1a; 原型模式通过复制已经存在的实例来返回新的实例&#xff0c;而不是新建实例&#xff0c;并且原型&#xff08;被复…

TextKit及应用

在iOS开发中我们常常使用UIKit的UITextView、UITextField、UILabel来显示文字。它们底层都是基于一个叫做TextKit的强大引擎。通过TextKit&#xff0c;我们可以方便地修改文字的样式和排版&#xff0c;而不需要直接操作复杂的Core Text。 1.什么是TextKit 在iOS7中&#xff0c…

判断类之间的父子关系

如何判断两个类之间的父子关系&#xff1f; java为我们提供了instanceof运算符&#xff0c;可以用来判断一个对象是否是否个类的实例&#xff0c;所以很容易的想到子类的对象肯定是父类的实例。但是如何所涉及到的类是不可实例化的该怎么办呢&#xff1f;好在java的Class为我们…

顺F速运,你被爱加M坑了

“ 顺F速运APP安全性分析。”之前的文章《Wireshark分析实战&#xff1a;某达速递登录帐号密码提取》对某达速递的APP进行了分析&#xff0c;该APP将数据完全暴露于网络流量中。于是对快递行业老大顺F速运的APP进行了分析&#xff0c;发现该APP质量还算上乘&#xff0c;但是&am…

[大数据之Spark]——Actions算子操作入门实例

Actions reduce(func) Aggregate the elements of the dataset using a function func (which takes two arguments and returns one). The function should be commutative and associative so that it can be computed correctly in parallel. 这个方法会传入两个参数&#x…

Runloop总结

1.什么是Runloop Runloop字面上翻译就是运行循环&#xff0c;也就是一直在转圈圈运行着&#xff0c;类似于do…while()。我们思考一个问题&#xff1a; 一个线程执行完成后就会退出&#xff0c;当我们启动一个iOS APP时&#xff0c;系统会调用main.m文件的main()函数: int m…

Android拷贝工程不覆盖原工程的配置方法

http://www.2cto.com/kf/201203/125131.html 在Eclipse中改包名的时候选择refactor-->rename,勾选Rename subpackages,这样就不需要一个个修改每个类中导入的包名了转载于:https://www.cnblogs.com/leihupqrst/p/3670224.html

顺F速运国际版,你的密码漏点了

“ 对顺F旗下各APP顺藤摸瓜分析——顺F速运国际版。”前文《顺F速运&#xff0c;你被爱加M坑了》提到&#xff0c;顺F速运APP使用爱加密加壳&#xff0c;流量中传输内容被加密并BASE64编码了&#xff0c;只是安全性不够&#xff0c;壳没有将顺丰的加密算法及密钥保护好。秉承避…

利用Injection插件加快Xcode编译速度

我们在调试iOS原生代码时&#xff0c;每次修改都需要CommandR来重新编译运行。当项目代码量很大&#xff0c;编译时间就会很漫长。因此对于开发中来说&#xff0c;如果能加快编译速度&#xff0c;能大大提高生产效率。如果我们能像Swift Playground、小程序或网页那样修改代码后…

存储过程的优缺点 (转载)

为什么要用存储过程 几个去 IBM 面试的兄弟回来抱怨&#xff1a;去了好几个不同的 IBM 项目组&#xff0c;几乎每个面试官问到数据库的时候都要问用没用过存储过程&#xff0c;烦人不&#xff1f;大家去面的程序员&#xff0c;又不是 DBA&#xff0c;以前的项目都没有用到存储…

计算机知识的学习

我不是计算机科班出生&#xff01; 大学里喜欢跟医电系的人混在一起&#xff0c;受到他们影响较多&#xff0c;开始喜欢上计算机&#xff01;win 98 Office 97 他们有的擅长C、有的擅长Flash、还有哥们喜欢硬件&#xff01; 西安的东六路是图书批发市场&#xff0c;我几乎每周…

Wireshark小技巧:将IP显示为域名

“ 本文介绍如何使Wireshark报文窗口的Source栏及Destination内的IP直接显示为域名&#xff0c;提升报文分析效率。” 一个典型的Wireshark界面如下&#xff1a; 从这个图里&#xff0c;能看到源IP及目的IP&#xff0c;在流量不大&#xff0c;数据不多的情况下&#xff0c;我…

个人学习某个系统或平台的3问式的整理和细化指引

i:三问&#xff1a;是什么&#xff1f;为什么&#xff1f;怎么样&#xff1f; ii:详细化问题指引&#xff1a;是什么的目的在于确定系统的大致范围&#xff0c;明确目标&#xff1a;->平台的主要功能是什么&#xff1f;业务流程是怎样的&#xff1f;业务范围有多大&#xff…

给iOS开发者的React Native入门使用教程

目录一. 原生iOS项目集成React Native二. 原生跳转RN页面三. 显示豆瓣热门电影列表四. 改为导航五.完整源代码一. 原生iOS项目集成React Native 创建一个新的文件夹&#xff0c;如RNProject&#xff0c;然后新建一个/ios的子文件夹&#xff0c;将已有的iOS项目全部文件复制进去…

PHP Memcached应用实现代码

肖理达 (KrazyNio AT hotmail.com), 2006.04. 06, 转载请注明出处 一、memcached 简介 在很多场合&#xff0c;我们都会听到 memcached 这个名字&#xff0c;但很多同学只是听过&#xff0c;并没有用过或实际了解过&#xff0c;只知道它是一个很不错的东东。这里简单介绍一下&a…

顺F分享,你是在裸奔吗?

“ 对顺F旗下各APP顺藤摸瓜分析——顺F分享。”前文对顺F速运和顺F速运国际版进行了分析&#xff0c;二者使用同一套接口&#xff0c;虽然保护强度不高&#xff0c;但对代码和数据的保护却区别对待&#xff0c;实在让人诧异。秉承避免浪费的原则&#xff0c;我们将持续对顺F旗下…