--------------android培训、java培训、学习型技术博客、期待与您交流! --------------
一、泛型(Generic)
泛型:就是指在对象建立时不指定类中属性的具体类型,而由外部在声明及实例化对象时指定具体的类型。
由于添加的元素类型不同,所以元素类型会进行自动提示为Object,所以取出集合中的元素时需要强转,但是类型各异,所以就会产生ClassCastException(类型转换异常),为了避免该种潜在威胁,就出现了一种安全机制--泛型。
泛型只是编译时的概念,是功编译器进行语法检查用的。主要目的有两个方面:
1、努力将运行时异常转换成编译时错误,减少运行时异常数量。
2、解决模板编程的问题。
泛型采用的是<>标记,在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型 ,其实<>就是一个用于接收具体引用数据类型的参数范围,如ArrayList中存入的元素时String,定义格式为: List<String> list = new ArrayList<String>();。
泛型的原理:
1、泛型的擦除:由于泛型是从jdk1.5开始的,所以为了兼容前面的类加载器,所以编译后的字节码是不存在泛型的概念的。
2、泛型的补偿机制:泛型已经擦除,所以就产生了一种补偿机制,使用元素的getClass方法,得到类型后,就不需要强转了。
泛型是实现是在编译时期检查类型,当类型检查完成后,就进行泛型擦除,然后再进行泛型补偿,达到将运行时异常转化为编译时异常,与减少强转的目的。
类型参数中的通配符约束
<?>允许所有泛型的引用调用,参数未指定泛型默认为<?>
<? extends 类名>只允许该类及其子类的引用调用
<? super 类名>只允许该类以及其父类的引用调用
<? extends 接口名>只允许该接口以及该接口的实现类的引用调用
泛型方法中的通配符约束
<?>允许所有泛型的引用调用
<? extends 类名>只允许该类及其子类的引用调用
<? extends 接口名>只允许该接口以及该接口的实现类的引用调用
<? extends 接口名&类名>只允许泛型为继承该类又实现该接口的类
泛型类中的通配符约束与泛型方法相同
编写泛型类需要注意的地方:
1、静态方法中不能使用类的泛型,因为泛型类中的泛型在创建类的对象是被替换为确定类型。静态方法可以通过类名直接访问,还没有传入参数,因此会报错。
2、不能再Catch字句中使用泛型,因为编译时,如果try字句抛出的是已检查一次,编译器无法确定Catch中能不能捕获该异常。
泛型方法定义示例
package cn.itheima.blog7;
import java.util.ArrayList;
import java.util.List;
public class GenericMethodDefine {
/**
* 自定义泛型方法演示(下限,上限等)
*/
public static void main(String[] args) {
//创建三个对象数组
Number[] a1 = new Number[2];
Double[] a2 = new Double[2];
String[] a3 = new String[2];
//创建两个集合
List<Number> l1= new ArrayList<Number>();
List<String> l2 = new ArrayList<String>();
//将数组中的数据添加到集合中
array2List(a1, l1);
array2List(a2, l1);
array2List(a3, l2);
}
//Number及其子类
public static <T extends Number> void array2List(T[] a, List<T> l){
for(T o : a){
l.add(o);
}
System.out.println(l);
}
//既是Number子类又是Comparable的实现类
/*public static <T extends Number & Comparable> void array2List(List<T> l, T[] a){
for(T o : a){
l.add(o);
}
}*/
//Comparable的实现类
public static <T extends Comparable> void array2List(T[] a, List<T> l){
for(T o : a){
l.add(o);
}
System.out.println(l);
}
}
二、注解(Annotation)
注解:相当于一个标记,加上了注解等于为程序加入了某种标记,开发工具和其他程序通过反射来了解类及个元素上有无标记,有标记就做该标记对应的操作。
注解的位置:加在包、类,字段,方法,方法的参数以及局部变量上
1、java.lang包中最基本的三种注解
@Override:主要用在方法覆盖时使用,用于保证方法覆盖的正确性。当子类覆盖方法不正确时,就会报错。
@Deprecated:用来声明一个不建议使用的方法,如果在程序中使用了该方法,则在编译时将出现过时警告,在Eclipse中会划线。随着Jdk的升级,许多方法会过时,但是为了顾及以前用该方法开发的项目,又不能删除该方法,但是又不建议使用,因此就会使用该注解。
@SuppressWarning:用来压制警告。在前面的泛型中,若一个类声明时没有指明泛型,则肯定在编译时产生警告,用了@SuppressWarning就不会显示这些警告。
@SuppressWarning中的关键字
2、自定义Annotation
定义格式:
[public] @interface 名称{
数据类型 变量名称();
}
在程序中只要使用了@interface声明Annotation,那么此Annotation实际上相当于实现了lang包下的Annotation接口
向Annotation中设置内容,即为注解添加属性
Annotation中设置内容类似接口中函数的定义,如添加一个name属性,类型为String,可写为[public] String name();,其中的public为默认值,就像接口默认为public abstract。也可以为设置的内容添加默认值,格式为[public] String name default 默认值。
自定义时,参数类型是有范围的, 八种基本数据类型,String,Class,枚举,注解及前面五种类型的数组。下面演示自定义简单注解及其使用:
package cn.itheima.blog7;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
enum Weekday{
SUN,MON,TUE,WEN,THU,FRI,SAT;
}
/*
* 演示String,基本数据,数组,class
*/
//@Retention的使用
@Retention(RetentionPolicy.RUNTIME)
//@Tatget的使用
@Target(value = ElementType.METHOD)
@interface ItheimaAnnotation {
String name();
int length();
Class clazz();
}
/*
* 演示数组,枚举,注解的使用,并设定默认值
*/
@interface ItheimaAnnotation_2{
//当数组中的值为一个时,可以不用花括号包装
int[] arrayAttr() default {1}/*1*/;
Weekday value();
SuppressWarnings warning() default @SuppressWarnings("deprecation");
}
package cn.itheima.blog7;
public class ItheimaAnnotationDemo {
/**
* 演示自定义Annotation的使用
*/
public static void main(String[] args) {
show();
}
@ItheimaAnnotation(name = "heima", length = 5, clazz = String.class)
//当属性为value,且其他属性有默认值时,可以省略value=不写,直接传入数值
@ItheimaAnnotation_2(Weekday.FRI)
public static void show(){
}
}
3、Retention和RetentionPolicy
在注解中,可以使用Retention定义一个注解的保存范围,Retention的定义中有一个value属性,类型为RetentionPolicy,RetentionPolicy中包含的范围有三种
(1)SOURCE:此注解类型的信息指挥保存在.java文件中,编译之后不保存。
(2)CLASS:此类型的注解保存在.java与.class文件中,此类使用时,信息不回加载到虚拟机中,如果注解在声明时未指定范围,默认为此范围。
(3)RUNTIME:此类型的注解保存在.java与.class文件中,也会加载到虚拟机。
4、Target注解,用来指定注解使用的位置
(1)public static final ElementType PACKAGE:只能用在包声明
(2)public static final ElementTypeFIELD:只能用在字段声明(包括枚举常量)上
(3)public static final ElementTypeANNOTATION_TYPE:只能用在注释类型声明上
(4)public static final ElementTypeCONSIRUCTOR:只能用在构造器声明上
(5)public static final ElementTypeMETHOD只能用在方法声明上
(6)public static final ElementTypePARAMETER只能用在参数声明上
(7)public static final ElementTypeTYPE只能用在类、接口、或枚举声明上
(8)public static final ElementType LOCAL_VARIABLE只能用在局部变量声明上
5、通过反射取得Annotation
步骤:
(1)取得运用注解的类
(2)取得类中运用注解的部分,如方法
(3)取得注解内容
结合前面自定义注解演示反射取得Annotation
package cn.itheima.blog7;
import java.lang.reflect.Method;
public class ReflectAnnotation {
/**
* @param args
* @throws Exception
* 获取Annotation指定属性的值
*/
public static void main(String[] args) throws Exception {
//得到运用注释的类
Class c = Class.forName("cn.itheima.blog7.ItheimaAnnotationDemo");
//得到类中运用注解的方法
Method method = c.getMethod("show");
//若指定的注释类型运用在此方法上
if(method.isAnnotationPresent(ItheimaAnnotation.class)){
//获取注释类型对象
ItheimaAnnotation ia = method.getAnnotation(ItheimaAnnotation.class);
//取值
String name = ia.name();
System.out.println(name);
}
}
}