泛型
什么是泛型?
泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
例如:GenericClass< T >{ }
一些常用的泛型类型变量:
- E:元素(Element),多用于java集合框架
- K:关键字(Key)
- N:数字(Number)
- T:类型(Type)
- V:值(Value)
泛型的相关使用:
- 集合中的使用,规范化数据,作用在编译期间
- 泛型可以作用在类的声明,接口的声明,方法的声明
- 在本类中当成已知类型的存在,是一个占位符
- 反射可以越过泛型检查,其实都是Object
泛型类
- 在定义类的时候使用泛型<T,E,K,V> 泛型符号名字和个数无要求,在本来中作为已知存在
- 在实例化的时候如果不指定类型,都当成Object来看
- 静态方法不能使用类的泛型占位符
public class CustomGeneric<T, A, B> {
private T name;
private A a;
private B b;
public T method1() {
return name;
}
public void method2(A a) {
System.out.println(a);
}
/*public static void method3(B b){//报错:静态方法不能使用类的泛型占位符
}*/
}
泛型接口
1、定义一个接口
public interface GenericInter<A, B, C> {
void methodA(A a);
void methodB(B a);
void methodC(C a);
}
2、实现接口的实现类分两种情况:
2.1 实现类指定泛型类型,在实现的时候接口泛型确定下来
public class GenericInterImpl implements GenericInter<String,Integer,Double> {
@Override
public void methodA(String s) {
}
@Override
public void methodB(Integer a) {
}
@Override
public void methodC(Double a) {
}
}
测试类:
@Test
public void methodA(){
GenericInterImpl genericInter = new GenericInterImpl();
}
2.2 现类不指定接口中泛型类型,继续把泛型占位符带上
- 类中的泛型占位符可以多于接口中定义的占位符个数,但是必须一一对应。
- new类的时候需要把类中定义的占位符一一对应的确定泛型
public class GenericInterImpl1<A,B,C,D> implements GenericInter<A,B,C> {
@Override
public void methodA(A a) {
}
@Override
public void methodB(B a) {
}
@Override
public void methodC(C a) {
}
}
测试类:
@Test
public void methodA(){
GenericInterImpl1<String, Integer, Double, String> impl1 = new GenericInterImpl1<>();
}
总结:
使用的时候,如果不指定泛型类型,统统都当成Object来看
泛型方法
泛型是在进行方法调用时 根据传入的值来确定类型的
public class GenericMethod {
public <T> T methodA(T t) {
return t;
}
/**
* 静态方法不能使用类的泛型占位符
* @param b
* @param <B>
*/
public static <B> void methodB(B b) {
}
}
泛型的上下限
<? extends T> 泛型的上限为T类型 只能是T类型或者T类型的子类
<? super T> 泛型的下限为T类型 只能是T类型或者T类型的父类
泛型中的约束和局限性:
1,不能实例化泛型类
2,静态变量或方法不能引用泛型类型变量,但是静态泛型方法是可以的
3,基本类型无法作为泛型类型
4,无法使用instanceof关键字或==判断泛型类的类型
5,泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的
6,泛型数组可以声明但无法实例化
7,泛型类不能继承Exception或者Throwable
8,不能捕获泛型类型限定的异常但可以将泛型限定的异常抛出
注解
Java 注解(Annotation)又称 Java 标注,是 JDK1.5 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
注解只是一个特殊的标记,注解需要有解析程序:
- 编译期间解析(编译器内部已经写好了)
- 运行期间解析(使用反射进行解析)
内置注解(JDK提供的注解)
- @Override:重写方法;编译器只要识别到此注解,检查子类的方法和父类的方法签名是否一致
- @Deprecated:即将抛弃,不建议使用的
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
- @SuppressWarnings:指示编译器去忽略注解中声明的警告。
元注解
元注解顾名思义我们可以理解为注解的注解,它是作用在注解中,方便我们使用注解实现想要的功能。元注解分别有@Retention、 @Target、 @Document、 @Inherited和@Repeatable(JDK1.8加入)五种。
- @Target:表示注解可以标记在哪些类型上面(可以有多个值)
- @Retention:注解的保留期
- @Doucumented 在生成java API文档的时候,加入注解内容
- @Inherited 表示子类可以继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解
@Target的取值
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE, //类上,接口上,枚举类
/** Field declaration (includes enum constants) */
FIELD, // 成员变量
/** Method declaration */
METHOD, //方法
/** Formal parameter declaration */
PARAMETER, //参数
/** Constructor declaration */
CONSTRUCTOR,// 构造方法
/** Local variable declaration */
LOCAL_VARIABLE, //局部变量
/** Annotation type declaration */
ANNOTATION_TYPE, //注解类型
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
@Retention的取值
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE, // 表示注解在源码中存在,编译器一旦编译完成,丢弃此注解,不会存在与class文件中
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS, // 当类加载器把class文件加载到内存中的时候会丢弃此注解
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME // 在运行期间依然保留此注解
}
自定义注解
1、自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
//使用的时候可以不给值,使用此默认值
String name() default "tom";
//当成员只有一个的时候,名字是value
int value();
}
注解的成员数据类型:
- 所有的基本数据类型,
- String,
- Class,
- Enum,
- 注解类型,
- 以上数据类型的数组
如果使用了其他数据类型,编译器将会丢出一个编译错误,注意,声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时还应该注意到注解也可以作为元素的类型,也就是嵌套注解。
注解不支持继承
注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口
2、自定义的注解的使用
public class Son extends Father {
private String name;
@CustomAnnotation(value = 18)
public void method() {
}
使用注解的注意点:
- 如果注解只有一个参数,最好取名value,这样在使用的时候可以直接指定属性值。例如:@Test(“hello”)
- 只能使用public和默认权限修饰符修饰参数
- 参数默认值:注解参数必须有确定的值。要么在定义的时候给默认值;要么在使用注解的时候指定参数值。
使用注解模拟Junit4测试
1、自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DivTest {
}
2、在类上进行注解
public class Student {
@DivTest
public void method1(){
System.out.println("method1执行了");
}
public void method2(){
System.out.println("method2执行了");
}
@DivTest
public void method3(){
System.out.println("method3执行了");
}
public void method4(){
System.out.println("method4执行了");
}
@DivTest
public void method5(){
System.out.println("method5执行了");
}
}
3、对程序进行解析,通过反射,凡是方法上面有注解的,进行方法调用
public class ReflectionDivTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class<?> aClass = Class.forName("com.codeyancy.test.Student");
Object o = aClass.newInstance();
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(DivTest.class)) {
declaredMethod.invoke(o);
}
}
}
}
结果:
带有注解的方法执行了!