泛型:
首先要知道什么是泛型,为什么要学习泛型以及怎么使用
1.什么是泛型
泛型主要用于规范类型,数据类型被指定成了一种参数,可以用在类、方法、接口上。及参数化类型
2.为什么要学习泛型
可以指定返回的数据类型避免强制转换造成的类型转换异常
演示:没有规范类型强转String导致转换异常
/**
* 位置实体类
* 属性x y
*/
public class Point {
private Object x;
private Object y;
public Point() {
}
public Point(Object x, Object y) {
this.x = x;
this.y = y;
}
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 class Text01 {
public static void main(String[] args) {
Point x = new Point("东京180",10);
String x1 = (String) x.getX();
String y1 = (String) x.getY();
}
}
报错信息:Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at com.gjc.demo1.Text01.main(Text01.java:7)
3.泛型的使用
3.1泛型类
类引入了一个类型变量T,用尖括号(<>)括起来,并放在类名的后面。
使用泛型类解决以上问题
/**
* 泛型类 <T> 在java中T一般表示任意类型 ,E表示集合的元素类型,K和V分别表示键和值的类型
* 属性x y
*/
public class Point<T> {
private T x;
private T y;
public Point() {
}
public Point(T x, T y) {
this.x = x;
this.y = y;
}
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;
}
}
在创建对象时直接规范了返回类型为String,避免了强转
public class Text01 {
public static void main(String[] args) {
//确定泛型的具体数据类型。即在创建对象的时候确定泛型。
Point<String> stringPoint = new Point<String>("东京180","北纬25");
String x = stringPoint.getX();
String y = stringPoint.getY();
}
}
3.2通配符
<?>主要接收未知的泛型类型
/**
* 泛型类
* @param <T> 任意类型
*/
class Info<T>{
private T var;
public void show(){
System.out.println("var========"+var);
}
public Info(T var) {
this.var = var;
}
}
public class Text02 {
public static void main(String[] args) {
Info<String> info1 = new Info<>("hello");
Info<Integer> info2 = new Info<>(52);
run(info1);
run(info2);
}
public static void run(Info<?> info){ //<?> 通配符 表示可以为Info<任意类型>
info.show();
}
}
运行结果:
var========hello
var========52
可以看到不管是什么类型都可以接收
3.3受限泛型
在传递时限制了泛型的范围,使用extends关键字修饰为上限,限制了类型只能接收该类型及其子类型。使用super关键字修饰为下限,限制了类型只能接收该类型以及父类
上限:声明对象 类名称<? extends 范围类>对象名称
上限:声明类 【访问修饰符】 类名称<T extends 范围类>{}
只能接收该类型及其子类型
下限:声明对象 类名称<? super 范围类>对象名称
下限:声明类 【访问修饰符】 类名称<T super 范围类>{}
只能接收该类型及其父类
public class Text03 {
public static void main(String[] args) {
Info<Object> a = new Info<>();
//必须为Number类或Number父类
run01(a); //Object为所有类的父类 可以
Info<Number> a2 = new Info<>();
run01(a2); //Number本身 可以
Info<Integer> a3 = new Info<>();
//必须为Number类或Number子类
run02(a3); //Integer为Number类的子类 可以
run02(a2); //Number本身 可以
}
//设置下限最小为Number
public static void run01(Info<? super Number> info){
info.show();
}
//设置上限最大为Number
public static void run02(Info<? extends Number> info){
info.show();
}
}
3.4泛型接口
泛型接口与泛型类的定义及使用基本相同
格式:修饰符 interface 接口名 <泛型标识>{
}
/**
* 泛型接口
* @param <T>
*/
interface Usb<T>{
public void use(T t);
}
有两种实现方式:
第一种实现时指定泛型的类型
class Mouse implements Usb<String>{
@Override
public void use(String s) {
//指定类型为String
System.out.println("设置类型"+s);
}
}
第二种不确定泛型的类型,直到创建对象时,确定泛型的类型
class Upan<T> implements Usb<T>{
@Override
public void use(T t) {
System.out.println("没有设置类型"+t);
}
}
//创建对象时才指定类型
Upan<Integer> a = new Upan<>();
a.use(10);
Upan<String> a2 = new Upan<>();
a2.use("哈哈哈");
3.5泛型方法
泛型方法可以在是泛型类中,也可以不在泛型类,泛型方法定义格式
【访问权限】 <泛型标识> 泛型标识 方法名称(泛型标识 参数名){
}
public <T> T aa(T t){
System.out.println(t);
return t;
}
也可以多个类型
public <T,E> T aa(T t,E e){
System.out.println(t+"=="+e);
return t;
}
Text04 text04 = new Text04();
text04.aa("aa",11); //aa==11
text04.aa(11,11); //11==11
注解
注解的格式:@注解名,可以添加参数。注解和注释类似,都用于做出解释,注解给程序看,注释用来给人看,注解是继承了Annotation接口的接口
1.预定义注解
是java已经定义好的注解,可以直接使用,比如:
@Override:重写,使用该注解时,需要保证有继承关系,子类覆写父类的方法,方法名、参数、返回值要保持一致,修饰符不能小于父类的。从大到小依次 public->protected->默认->private 子类抛出的异常不能大于父类。否则会产生编译器错误
@Deprecated:可以修饰类、方法或字段。标识已经过时,不建议使用
@FuncationInterface: 函数式接口。要求接口有且仅有一个抽象方法
2.自定义注解
自定义注解是可以由开发者自己定义的注解
语法:
public @interface 注解名{
}
//默认可以放在任何地方使用
public @interface zj {
}
@zj
public class Text06 {
@zj
private String aaa;
@zj
private void run(@zj String bbb){
System.out.println(bbb);
}
}
2.1自定义注解--属性
可以看作是注解的成员变量,使用注解时可以为这些属性赋值
格式:
public @interface 注解名{
数据类型 属性名() default 默认值;
//如果没有设置默认值,则使用时必须给定值
} 数据类型为基本数据类型和字符串类型,以及这些类型的数组
public @interface Zidiyi{
String value() default "";
int age() default 0;
String[] hobby() default {"唱","跳"};
}
这里定义了一个为Zidiyi的注解,并设置了两个属性,并设置了默认值,我们可以在下面代码中使用这个注解
@Zidiyi(value = "asd",age = 20,hobby = {"rap","篮球"})
public class Text07 {
@Zidiyi()//默认值value="",age=0,hobby={"唱","跳"}
public void text(){
}
}
3.元注解
用来修饰注解的注解称为元注解
@Retention:限制注解什么时候生效。
SOURCE 源码中生效
CLASS 字节码时生效
RUNTIME 运行时生效 可以被反射读取
@Retention(RetentionPolicy.SOURCE)//源码时生效
@Retention(RetentionPolicy.CLASS)//字节码时生效
@Retention(RetentionPolicy.RUNTIME)//运行时生效 使用最多
@Target:限制注解可以使用在什么地方
TYPE 使用在类上
FIELD 使用在属性上
METHOD 使用在方法上
PARAMETER 使用在参数上
CONSTRUCTOR 使用在构造方法上
//可以用在类、方法和参数上
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER})
public @interface Zidiyi{
}
@Zidiyi() //类上
public class Text07 {
@Zidiyi()// 方法上
public void text(@Zidiyi String a){ //参数上
}
}
@Doucumented指定注解是否包含在api文档中,默认不会包含在api文档中,如果需要被包含在javaDoc中,就需要只用@Documented注解
//包含api文档
@Documented
//可以用在类、方法和参数上
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)//运行时生效 使用最多
public @interface Zidiyi{
String value() default "";
int age() default 0;
String[] hobby() default {"唱","跳"};
}