三个你知道却不了解的细节

泛型
是什么?
泛型是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 {
}