Java学习笔记(二一)——Java 泛型
【前面的话】
最近脸好干,掉皮,需要买点化妆品了。
Java泛型好好学习一下。
【定义】
一、泛型的定义主要有以下两种:
- 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
- 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
不论使用那个定义,泛型的参数在真正使用泛型时都必须作出指明。
二、使用泛型的目的:
- 一些强类型程序语言支持泛型,其主要目的是加强类型安全及减少类转换的次数,但一些支持泛型的程序语言只能达到部份目的。
- 泛型程序设计(Generic programming)意味着编写的代码可以被很多不同类型的对象所重用。
- 是对java语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值得占位符一样。
【Java泛型的几种类型代码】
一、不使用泛型的代码
我们定义一个Person类,包含三个属性x,y,z。在我们开始定义地时候,我们也不知道这三个属性是用来干什么的,所以我们定义为Object类型。但是在使用的时候,我们分别对x,y,z赋予了int,double,String类型,所以在取出的时候,我们需要把这三个类型值进行强制转换。如下代码:
1. Person.java
public class Person {
private Object x;
private Object y;
private Object z;
//使用Object类型。可以转化为任何类型
public Object getX() {
return x;
}
public void setX(Object x) {
this.x = x;
}
public Object getY() {
return y;
}
public void setY(Object y) {
this.y = y;
}
public Object getZ() {
return z;
}
public void setZ(Object z) {
this.z = z;
}
}
2. NoGenericTest.java
public class NoGenericTest {
public static void main(String[]args){
Person boy=new Person();
boy.setX(20);
boy.setY(22.2);
boy.setZ("帅哥TT");
//这里根据设置的不同类型的值,我们需要进行强制类型转化。
int x=(Integer)boy.getX();
double y=(double)boy.getY();
String z=(String)boy.getZ();
System.out.println(x);
System.out.println(y);
System.out.println(z);
}
}
3. 运行结果
20
22.2
帅哥TT
二、使用一个类型变量泛型的代码
我们定义一个泛型类Person,定义三个属性x,y,z,在测试类中,我们设置属性的值,并打印。
1. Person.java
public class Person<T> {
private T x;
private T y;
private T z;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
public T getZ() {
return z;
}
public void setZ(T z) {
this.z = z;
}
}
2. GenericTest.java
public class GenericTest {
public static void main(String[]args){
Person boy=new Person();
boy.setX(20);
boy.setY(22.2);
boy.setZ("帅哥TT");
//不用进行类型转化
System.out.println(boy.getX());
System.out.println(boy.getY());
System.out.println(boy.getZ());
}
}
3. 运行结果
20
22.2
帅哥TT
三、使用两个类型变量泛型的代码
我们定义一个泛型类Person,定义两个属性x,y,使用了两种不同的类型变量,在测试类中,我们设置属性的值,并打印。
1. Person.java
public class Person<T1,T2> {
private T1 x;
private T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
}
2. GenericTest.java
public class GenerricTest {
public static void main(String[] args){
Person<String,Integer> boy=new Person<String,Integer>();
boy.setX("帅哥TT");
boy.setY(20);
System.out.println(boy.getX());
System.out.println(boy.getY());
}
}
3. 运行结果
帅哥TT
20
四、使用泛型的继承
我们定义一个泛型类Person,定义两个属性x,y,然后定义另一个泛型类Boy,定义属性z,Boy继承Person类,在测试类中,我们设置属性的值,并打印。
1. Person.java
public class Person<T1,T2> {
private T1 x;
private T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
}
2. Boy
public class Boy<T1,T2,T3>extends Person<T1,T2> {
private T3 z;
public T3 getZ() {
return z;
}
public void setZ(T3 z) {
this.z = z;
}
}
3. GenericTest.java
public class GenericTest {
public static void main(String[] args){
Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
boy.setX("帅哥TT");
boy.setY(20);
boy.setZ(200000.22);
System.out.println(boy.getX());
System.out.println(boy.getY());
System.out.println(boy.getZ());
}
}
4. 运行结果
帅哥TT
20
200000.22
五、使用泛型的接口
我们定义一个泛型接口Person,定义两个方法,然后定义另一个泛型类Boy,实现泛型接口Person,定义属性x,y,z,在测试类中,我们设置属性的值,并打印。
1. Person.java
public interface Person<T1,T2> {
public T1 getX();
public T2 getY();
}
2. Boy
public class Boy<T1,T2,T3>implements Person<T1,T2> {
private T1 x;
private T2 y;
private T3 z;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
public T3 getZ() {
return z;
}
public void setZ(T3 z) {
this.z = z;
}
}
3. GenericTest.java
public class GenericTest {
public static void main(String[] args){
Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
boy.setX("帅哥TT");
boy.setY(20);
boy.setZ(200000.22);
System.out.println(boy.getX());
System.out.println(boy.getY());
System.out.println(boy.getZ());
}
}
4. 运行结果
帅哥TT
20
200000.22
六、使用泛型方法
说明一下,定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。
定义一个普通类Person,定义一个泛型方法,如下代码:
1. Person.java
public class Person{
public static<T>T getMiddle(T[]a){
return a[a.length/2];
}
public static void main(String [] args){
String[]name={"帅哥TT","帅哥TT1","帅哥TT2"};
String middle=Person.<String>getMiddle(name);
System.out.println(middle);
Integer[]num={20,22,25};
Integer middle1=Person.<Integer>getMiddle(num);
System.out.println(middle1);
Double[]num1={20.0,22.2,25.5};
Double middle2=Person.<Double>getMiddle(num1);
System.out.println(middle2);
}
}
2. 运行结果
帅哥TT1
22
22.2
七、类型变量的限定
如下代码,我们在方法min中定义了一个变量smallest类型为T,这说明了smallest可以是任何一个类的对象,我们在下面的代码中需要使用compareTo方法, 但是我们没有办法确定我们的T中含有CompareTo方法,所以我们需要对T进行限定,在代码中我们让T继承Comparable类。如下:
public static<T extends Comparable>T min(T[]a)
1. Person.java
public class Person{
public static<T extends Comparable>T min(T[]a){
if(a==null||a.length==0){
return null;
}
T smallest=a[0];
for(int i=1;i<a.length;i++){
if(smallest.compareTo(a[i])>0){
smallest=a[i];
}
}
return smallest;
}
public static void main(String [] args){
Integer[]num={20,25,30,10};
Integer middle=Person.<Integer>min(num);
System.out.println(middle);
}
}
2. 运行结果
10
【Java泛型理解】
一、类型擦除
正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。
很多泛型的奇怪特性都与这个类型擦除的存在有关,包括:
- 泛型类并没有自己独有的Class类对象。比如并不存在List<String>.class或是List<Integer>.class,而只有List.class。
- 静态变量是被泛型类的所有实例所共享的。对于声明为MyClass<T>的类,访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过new MyClass<String>还是new MyClass<Integer>创建的对象,都是共享一个静态变量。
- 泛型的类型参数不能用在Java异常处理的catch语句中。因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常对应的catch语句。
二、最佳实践
在使用泛型的时候可以遵循一些基本的原则,从而避免一些常见的问题。
- 在代码中避免泛型类和原始类型的混用。比如List<String>和List不应该共同使用。这样会产生一些编译器警告和潜在的运行时异常。当需要利用JDK 5之前开发的遗留代码,而不得不这么做时,也尽可能的隔离相关的代码。
- 在使用带通配符的泛型类的时候,需要明确通配符所代表的一组类型的概念。由于具体的类型是未知的,很多操作是不允许的。
- 泛型类最好不要同数组一块使用。你只能创建new List<?>[10]这样的数组,无法创建new List<String>[10]这样的。这限制了数组的使用能力,而且会带来很多费解的问题。因此,当需要类似数组的功能时候,使用集合类即可。
- 不要忽视编译器给出的警告信息。
【参考资料】
- Java深度历险(五)——Java泛型
- java核心技术
【后面的话】
好好学习。
——TT
Java学习笔记(二一)——Java 泛型的更多相关文章
- Java学习笔记二十:Java中的内部类
Java中的内部类 一:什么是内部类: (1).什么是内部类呢? 内部类( Inner Class )就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. (2).那为什么要将一个类定 ...
- Java学习笔记二十七:Java中的抽象类
Java中的抽象类 一:Java抽象类: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就 ...
- Java学习笔记二十一:Java面向对象的三大特性之继承
Java面向对象的三大特性之继承 一:继承的概念: 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类. 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方 ...
- Java学习笔记二十三:Java的继承初始化顺序
Java的继承初始化顺序 当使用继承这个特性时,程序是如何执行的: 继承的初始化顺序 1.初始化父类再初始子类 2.先执行初始化对象中属性,再执行构造方法中的初始化 当使用继承这个特性时,程序是如何执 ...
- 学习笔记(二)--->《Java 8编程官方参考教程(第9版).pdf》:第七章到九章学习笔记
注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法者自负一切 ...
- Java学习笔记心得——初识Java
初识Java 拿到这本厚厚的<Java学习笔记>,翻开目录:Java平台概论.从JDK到TDE.认识对象.封装.继承与多态...看着这些似懂非懂的术语名词,心里怀着些好奇与担忧,就这样我开 ...
- Java学习笔记之使用反射+泛型构建通用DAO
PS:最近简单的学了学后台Servlet+JSP.也就只能学到这里了.没那么多精力去学SSH了,毕竟Android还有很多东西都没学完.. 学习内容: 1.如何使用反射+泛型构建通用DAO. 1.使用 ...
- Java基础学习笔记二十三 Java核心语法之反射
类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任 ...
- Java学习笔记二--API课堂记录
JavaSE课堂记录(二) 第一节课 方法的重载:方法名相同,参数列表不同 方法的重写:方法名,参数列表相同 两同:方法名相同,参数列表相同 两小:访问权限小与等于父类,返回值类型小于等于父类(返回值 ...
- Java学习笔记二.1
和其他高级语言类似,Java也具有以下部分 1.关键字:见下表,注意Java严格区分大小写,关键字都是小写 2.标识符:见下图 3.注释.有两种://(单行注释)和/**/(多行注释).还有一种文档注 ...
随机推荐
- textarea文本简单样式编辑
第一种方法采用替换:就是将文本域的换号符号\r\n,替换成其他符号,存入数据库,然后显示的时候再转换回来: //转换换行符$str=preg_replace("/\r\n|\r|\n/&qu ...
- web.xml文件报错:cvc-complex-type.2.4.a: Invalid content was found starting with element 'init-param'.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" ...
- [转]excel set drop-down values based on vlookup
本文转自:http://stackoverflow.com/questions/10657811/set-drop-down-values-based-on-vlookup 问: I have a w ...
- uva 839 Not so Mobile-S.B.S.
Before being an ubiquous communications gadget, a mobilewas just a structure made of strings and wir ...
- 用javascript得到客户端IP的新方法
javascript得到客户端IP的新方法 很久以来,我都是经过http://fw.qq.com/ipaddress来得到客户端用户的IP,这个方法简单.快速.实用 . 我们调用它的写法是: < ...
- ZBrush中如何才能快速完成脸部雕刻(上)
骨骼,是一门基础艺术,几百年来一直为伟大的艺术大师所研究,它曾经,也将一直是创作现实且可信角色的关键,提高骨骼知识更将大大提高雕刻技能. 查看更多内容请直接前往:http://www.zbrushcn ...
- 怎么运用ZBrush中的Z球制作身体部分
本文主要讲解利用ZSphere[Z球]创建人体的大体轮廓,这是ZBrush®软件制作模型的第一步,这一阶段中我们需要反复调节人体比例结构,为以后深入制作打下基础,具体制作过程如下. 查看更多内容请直接 ...
- 如何用ZBrush确定头部五官的位置
之前和大家讲过如何在ZBrush中确定头部雕刻,在第一阶段中面数很少,只能将大型体雕刻出来,由于面数太少不能进行下面的雕刻.接下来第二阶段的调整,需要重新分布模型表面的网格,增加面数,进行五官的位置的 ...
- codeforces 487B B. Strip(RMQ+二分+dp)
题目链接: B. Strip time limit per test 1 second memory limit per test 256 megabytes input standard input ...
- EDM总结
1. 使用table 布局,align="center" 2. 尽量不要用padding-left ,padding-right 样式: 3. CSS的浮动定位: 4. 不 ...