Java 地基(十)----(泛型、枚举、注解 )三个你知道却会忽略的知识点


在这里插入图片描述

泛型

是什么?

泛型是JDK1.5及以上才可以使用的特性/语法,它的本质是 类型参数化(Parameterized by types).

有什么用?

当我们在声明 类 接口 方法时要确定参数类型或者返回值类型,我们不想写死,一成不变,那么泛型就出现了,他可以使得代码的参数类型以及返回值类型变得灵活,由你而定!

如何使用?

public class Point<T>{
	T x;
	T y;
}
Point<Integer> p1 = new Point<Integer>();
p1.x = 1;
p1.y = 2;
  • 注意,T是泛型参数,表示一种数据类型,具体是什么类型,需要将来使用Point的时候进行传参来确定
  • 注意,如果将来Point在使用的时候,没有给泛型参数T传值,那么T默认就表示为Object类型
  • 注意,T是泛型参数的名字,也就是相当于形参,名字随便起,但是一般用一个有意义的大写字母
  • 注意,给泛型参照传的值,只能是引用类型,不能是基本类型: Point 编译报错

进阶集合泛型的使用

Collection c = new ArrayList();
c.add("hello1");
c.add("hello2");
c.add("hello3");
c.add(1);
for(Object obj:c){
	String str = (String) obj;
	System.out.println(str);
}
/
/运行结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot
be cast to java.lang.String

集合默认可存储各种类型,那么之后就会出现类型转换的问题

因此给集合添加泛型,强制只能添加某一种数据类型

Collection c = new ArrayList<>()

分类

  • 泛型类
    • public class Point{…}
  • 泛型接口
    • public interface Action{…}
  • 泛型方法
    • public T test(T t){…}

错误分析

//编译通过
//父类型的引用,指向子类对象
Object o = new Integer(1);

//编译通过
//Object[]类型兼容所有的【引用】类型数组
//arr可以指向任意 引用类型 数组对象
Object[] arr = new Integer[1];

//编译失败
//注意,这个编译报错,类型不兼容
//int[] 是基本类型数组
//arr = new int[1][1]则通过
Object[] arr = new int[1];

//编译失败
//错误信息:ArrayList<Integer>无法转为ArrayList<Object>
//在编译期间,ArrayList<Integer>和ArrayList<Object>是俩个不同的类型,并且没有子父类型的关系,无多态概念
//虽然 Integer 是 Object 的子类型,但是 ArrayList<Integer> 和 ArrayList<Object> 之间没有子父类型的关系,它们就是俩个不同的类型  
ArrayList<Object> list = new ArrayList<Integer>();

注意,=号俩边的所指定的泛型类型,必须是要一样的
注意,这里说的泛型类型,指的是<>中所指定的类型

通配符

  • List , 这个 T 是一个形参,可以理解为一个占位符,被使用时,会在程序运行的时候替换成具体的类型,比如替换成String,Integer之类的。

  • List <?>, 这个 ? 是一个实参,这是Java定义的一种特殊类型,比Object更特殊,就像一个影子。比如List和List是没有父子关系的,这是两个类型,List类型和List类型;但是List<?> 是 List的父类。

  • 用数学集合的语言来表述,? 表示了集合【所有Java类型,String,Integer等系统定义的,或者用户定义的Foo等类型】这个整体;而 T 表示了集合【所有Java类型,String,Integer等系统定义的,或者用户定义的Foo等类型】中的一个成员。

  • 正是因为 ? 是个集合,T 是集合中的一个成员,导致我们很容易混淆这两者到底有什么区别。在一些情况下,这两者确实是可以相互替换的,但是另一种情况下就不行了。

  • ?表示了任何的一种类型,那 List<?> 岂不是可以包含 String 和 Integer,但这又和Java的类型系统矛盾了,List里面只能放一种类型。于是乎,对于 List list 是不可能进行 list.add(1) 的,不能对它进行写操作,除了可以 list.add(null)

意义:在我不需要处理数组里的元素的时候,写代码更方便简单。如下两个方法的功能是一样的,但是使用List<?>简单些

T和?的区别

  • 两者都可以通过extends来限定一个类型的子集,但是 T 可以 List 即限定为多重继承的,? 却不可以 List<T extends Number & ExtendInterface>
  • 使用super限定父集的时候,? 可以, T 不可以
  • 虽然使用通配符(?)的集合,不能再往其中添加数据了,但是可以遍历集合取出数据

泛型边界

extends:类接口,方法,变量

  • 例如: List<? extends Number> list
  • 将来引用list就可以接收泛型是 Number 或者 Number 子类型的List集合对象

super :只在变量中使用

  • 例如: List<? super Number> list
  • 将来引用list就可以接收泛型是 Number 或者 Number 父类型的List集合对象

类型擦除:

泛型类型仅存在于编译期间,编译后的字节码和运行时不包含泛型信息,所有的泛型类型映射到同一份字节码。

注意,泛型信息被擦除后,所有的泛型类型都会统一变为原始类型:Object

//编译报错
//ArrayList<Integer>和new ArrayList<Long>在编译期间是不同的类型
ArrayList<Integer> list = new ArrayList<Long>();
//但是编译完成后,它们对应的是同一份class文件:ArrayList.class
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<Long> list2 = new ArrayList<Long>();
System.out.println(list1.getClass() == list2.getClass());//true

一些注意点

泛型的定义

泛型方法:要么方法上定义 要么类上面定义

无多态概念:=号俩边需要定义相同的

反解析:不完全解析 javap

类型擦除:Java让编译器擦除掉关于泛型类型的信息

字节码验证(注释和泛型不在字节码中)

可以看出,Java的泛型只存在于编译时期,泛型使编译器可以在编译期间对类型进行检查以提高类
型安全,减少运行时由于对象类型不匹配引发的异常。

枚举:

枚举的创建

public enum Color {
	BLACK, WHITE
}

通过自写代码去理解

完整的理解是单例模式,静态内部类创建对象加载。默认final,不可继承,固定个数和名称

public class Gender {
	//1.创建2个对象  两个对象名字固定
	private String name;
	/*
	 * public String getName() { return name; } public void setName(String name) {
	 * this.name = name; } public Gender(String name) { // TODO Auto-generated
	 * constructor stub }
	 */
	public static final Gender man;
	public static final Gender women;
	static {
		man = new Gender("男性");
		women = new Gender("女性");
		
	}

	private Gender(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		
		return "name"+name;
	}

}

对于枚举类的分析

  • 枚举其实也是一种类,同时还是一个final修饰的类
    • 是类当然可以定义属性和方法 注意,枚举类型中的第一行代码,要求一定是指定枚举对象的个数和名字,同时最后面加分号(;)
      在这行代码下, 才可以定义枚举类型的属性和方法
  • 枚举类型都会默认继承一个父类型: java.lang.Enum ,这还是一个抽象的泛型类 public abstract class Enum<E extends Enum<E>>{}
  • 枚举中所定义的对象,其实就是类里面的public static final修饰的常量,并且这些常量会在静态代码块中做初始化
  • 枚举类型中还一个默认的私有构造器,说明我们在外面并不能自己去创建枚举类型的对象
  • 枚举类型中还有默认添加进来的方法
    values() 方法,可以返回这个枚举类型的所有对象,返回类型是数组
    valueOf(String str) 方法,通过一个字符串可以返回枚举对象,这个字符串参数就是枚举对象的名字
  • 枚举类型会从父类中继承过来一些方法(具体可以查看其固定的父类型),例如
    String name(),返回这个枚举对象的名字
    int ordinal(),返回这个枚举对象的编号,默认从0开始

存在的合理性

一个类的对象,从意义上来说,对象个数是固定的。

定义为一个枚举类型(enum),我们就可以在枚举类型中,提前将这个类型的对象个数和对象名字都固定下来,并且之后的使用中不会改变,也不会再创建其他对象。

获取枚举对象

方式一:

public static void main(String[] args){
	//使用类名直接访问类中定义的俩对象
	//最常用的一种方式
	Gender g = Gender.MALE;
	g = Gender.FEMALE;
	//可以调用从父类型Enum以及Object中继承过来的方法
	System.out.println(g.name());
	System.out.println(g.ordinal());
	System.out.println(g.toString());
} 
//运行结果:
MALE
0 M
ALE

方式二:

public static void main(String[] args){
	//通过字符串参数的改变,可以获取到Gender中的指定的一个对象
	String name = "MALE";
	Gender g = Gender.valueOf(name);
	System.out.println(g.name());
	System.out.println(g.ordinal());
	System.out.println(g.toString());
	} 
//运行结果:
MALE
0 M
ALE

方式三:

public static void main(String[] args){
	//通过字符串确定是哪一个枚举类型
	Class c = Class.forName("com.briup.demo.Genger");
	//通过字符串确定是哪一个名字的枚举对象
	String name = "FEMALE";
	//可以通过改变字符串,获取到java中任何一个枚举类型中的任意一个枚举对象
	Enum g = Enum.valueOf(c,name);
	System.out.println(g.name());
    System.out.println(g.ordinal());
System.out.println(g.toString());
} 
//运行结果:
FEMALE
1 F
EMALE

什么时候使用

在项目中,只要一个类型的对象个数和名称能固定下来的,就可以考虑使用枚举类型来表示。

注解

是什么?

注解(Annotation),是jdk5.0引入的技术,用它可以对java中的某一个段程序进行说明或标注,并且这个注解的信息可以被其他程序使用特定的方式读取到,从而完成相应的操作。

//默认是public
public @interface 注解名称 {
	属性类型 属性名() default 默认值 ;
}

元注解又是什么?操作自写注解的

常用到的元注解有:

  • @Target,用于描述注解的使用范围,例如用在类上面还是方法上面
    • 另查
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
  • @Retention,用于描述注解的保存策略,是保留到源代码中、Class文件中、还是加载到内存中
  • @Documented,用于描述该注解将会被javadoc生产到API文档中
  • @Inherited,用于表示某个被标注的类型是被继承的,如果一个使用
  • @Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 只能类与类之间

注解的留存:在源代码、字节码、运行时

类中使用的注解,根据配置,可以保持到三个不同的阶段:

  • SOURCE,注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
  • CLASS,注解被保留到class文件,但jvm加载class文件时候被遗弃
  • RUNTIME,注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER,TYPE})
public @interface Deprecated {
}

注解的自定义使用:目前所学尚浅,待日后飞黄腾达

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值