Java基础知识-泛型、反射、注解
2022/1/16 17:35:00
本文主要是介绍Java基础知识-泛型、反射、注解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一、泛型
泛型是 Java 中另一个使用非常广泛的特性,泛型中的「泛」指的是参数类型不固定, 也就是说什么数据类型都可以,它还有另一个名字,就是「参数化类型」——也就是说不仅 数据本身是参数,数据类型也可以被指定为参数——类、接口或者方法可以被不同类型的参数所重用。你只需告诉编译器要使用什么类型,剩下的细节交给它来处理。
1、泛型类
通过泛型可以完成对一组类的操作对外开放相同的接口
//在实例化泛型类时,必须指定T的具体类型 public class Generic<T>{ //key这个成员变量的类型为T,T的类型由外部指定 private T key; public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 this.key = key; } public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 return key; } } //可以传入类型不同的参数 Generic generic = new Generic("1"); Generic generic1 = new Generic(1); Generic generic2 = new Generic(1.11); Generic generic3 = new Generic(false);
2、泛型接口
未传入泛型实参时,在声明类的时候,需将泛型的声明也一起加到类中。
public interface Generator<T> { public T next(); } class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } }
传入实参时:
public class FruitGenerator implements Generator<String> { private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; @Override public String next() { Random rand = new Random(); return fruits[rand.nextInt(3)]; } }
3、泛型方法
//可变参数 public <T> void printMsg( T... args){ for(T t : args){ Log.d("泛型测试","t is " + t); } } printMsg("111",222,"aaaa","2323.4",55.55);
4、通配符
- ?:表示通配符,它无法声明变量,但在泛型中却非常重要,它表示可以持有任何类型, 例如:List<?> list,表示 List 集合中可以存放任何数据类型(虽然它本来就可以这么做)。
- <? extends T>是上界通配符,表示只允许 T 及 T 的子类调用,所以如果传入的类型不 是 T 或 T 的子类,编译会报错。
- <? super T>是下界通配符,表示只允许 T 及 T 的父类调用,所以如果传入的类型不是 T 或 T 的父类,编译会报错。
练习1: 上界通配符有一个特性:只能获取,不能添加,请通过搜索引擎了解这句话的含义并 用代码实现它
答:上界只取不放是指当操作一个泛型参数为上界通配符<? extends T>的引用时,只能操作其返回泛型参数相关的方法(返回值为T的方法)而无法操作设置泛型参数相关(修改T)的方法。
练习2: 下界通配符有一个特性:只能添加,不能获取,请通过搜索引擎了解这句话的含义并 用代码实现它。
答:而下界只存不取则是指当操作一个泛型参数为下届通配符<? super T>的引用时,只能操作设置泛型参数相关(修改T)而无法操作返回泛型参数相关的方法(返回值为T的方法)。
package com.company; public class Task08_01 { public static void main(String[] args){ /* * 上界<? extends T>:不能往里存,只能往外取 * <? extends Fruit>会使盘子里放东西的set()方法失效,但是get()方法还有效 * */ Plate<? extends Fruit> p1 = new Plate<Apple>(new Apple()); /* * p.set(new Fruit()); 报错 * p.set(new Fruit());报错 */ //读取出来的东西只能存放在Fruit或者它的基类里 Fruit newFruit1 = p1.get(); Object newFruit2 = p1.get(); //Apple newFruit3 = p.get();报错 /* * 下界<? super T>:不影响往里存,但是往外取的只能放在Object对象里 * <? super Fruit>:get()方法获得的对象只能放在Object对象里,set()方法正常 * */ Plate<? super Fruit> p2 = new Plate<Fruit>(new Fruit()); //存入元素正常 p2.set(new Fruit()); p2.set(new Apple()); //取出来的东西只能放在Object对象中 Object newFruit4 = p2.get(); } static class Fruit{ } static class Apple extends Fruit{ } static class Plate<T>{ private T item; public Plate(T t){ item = t; } public void set(T t){ item = t; } public T get(){ return item; } } }
总结:
- 频繁往外读取内容的,适合用上界Extends。
- 经常往里插入的,适合用下界Super。
二、反射
Java 有一种特性,能够在程序运行时获取某个类对象的所有的属性、方法、包信息和 注解等等,并且还能改变对象的属性值、调用对象方法、验证注解信息,这种动态获取信息、 动态改变属性值和动态调用方法的能力,就是反射(Reflection)。反。反射是将类中各个成分映射成各个对象。
1、获取Class对象
- 通过类名获取:类名.class
- 通过对象获取:对象名.getClass();
- 通过全类名获取:Class.forName(类的全名),这里的类的全名指的是包括包名在内的 对象名称,例如:net.csdn.java.User,就是 User 类的全名
2、获取构造方法
- public Constructor[] getConstructors():获取所有公有的构造方法
- public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
- public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法
- public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有
3、获取成员变量
- Field[] getFields():获取所有的"公有字段"
- Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有
- public Field getField(String fieldName):获取某个"公有的"字段
- public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
- Field --> public void set(Object obj,Object value):设置字段的值
**练习:**使用反射解析某个你创建的类,把类中所有的属性、方法、接口都提取出来。要求: 通过三种不同的方式获取 Class 对象; 修改解析出来的类的属性值,然后再给类添加新的属性; 调用类方法(包括私有方法、静态方法和构造方法),执行后打印结果; 解析出该类的父类对象,并同样修改其父类的属性值并调用父类的方法。
package com.company; import java.awt.datatransfer.FlavorEvent; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Task09_反射 { public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //第一种方式获得Class对象 Student student1 = new Student(); Class stuClass = student1.getClass(); //第二种方式获得Class对象 Class stuClass2 = Student.class; //第三种方式获得Class对象 try{ Class stuClass3 = Class.forName("com.company.Student"); }catch(ClassNotFoundException e){ e.printStackTrace(); } //获取Student类的所有属性 System.out.println("获取所有属性:"); Field[] fieldArray = stuClass.getDeclaredFields(); for(Field f : fieldArray) System.out.println(f); //为属性设置值 System.out.println("为属性设置值:"); Field f = stuClass.getField("name"); Object obj = stuClass.getConstructor().newInstance();//产生Student对象 f.set(obj,"FXW"); Student stu = (Student)obj; System.out.println("验证名字"+stu.name); //调用构造方法 System.out.println("调用构造方法"); Constructor con = stuClass.getConstructor(null); System.out.println(con); Object obj2 = con.newInstance(); //调用私有方法 System.out.println("调用私有方法"); Method m = stuClass.getDeclaredMethod("show",int.class); System.out.println(m); m.setAccessible(true);//解除私有限定 Object result = m.invoke(obj,20); System.out.println(result); } } class Student{ public String name; protected int age; char sex; private String number; public Student(){ System.out.println("共有无参构造方法"); } public Student(String name,int age){ System.out.println("共有有参构造方法"); } private String show(int age){ System.out.println("私有方法"); return "a"; } }
三、注解
1、代码注解:作用在代码上
- @Override:检查该方法是否是重写方法,如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
- @Deprecated:标记过时方法,如果使用该方法,会报编译警告
- @SuppressWarnings:指示编译器去忽略注解中声明的警告
2、元注解:作用在其他注解上
- @Retention:标识注解怎么保存,是只在代码中,还是编入 class 文件中,或者是在 运行时可以通过反射访问
- @Documented:标记注解是否包含在用户文档中;
- @Target:标记注解应该是哪种 Java 成员
- @Inherited:标记注解是继承于哪个注解类(默认注解并没有继承于任何子类)
- @SafeVarargs:Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数 调用产生的警告
- @FunctionalInterface:Java 8 开始支持,标识一个函数式接口
- @Repeatable:Java 8 开始支持,标识注解可以在同一个声明上使用多次
练习1: 自定义一个 ElementType 类别为 METHOD,RetentionPolicy 类别为 RUNTIME 的 注解,任务要求:创建一个类,类的方法使用自定义的注解; 通过反射解析类注解,判断该方法是否有注解,如果有则打印出注解的内容。
注:
- RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
- RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
- RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
package com.company; import java.lang.annotation.*; import java.lang.reflect.Method; public class Task10_01 { public static void main(String[] args) throws NoSuchMethodException { //获取类 Class stuClass = Student.class; //获取方法 Method method = stuClass.getMethod("show"); //获取注解对象 M m = method.getAnnotation(M.class); System.out.println(m.value()); } @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD}) public @interface M { String value() default "这是默认注释内容"; } class Student{ @M public void show(){ System.out.println("a"); } } }
练习2: 自定义一个 ElementType 类别为 FIELD,RetentionPolicy 类别为 CLASS 的注解, 任务要求: 创建一个类,给类的某个字段使用自定义的注解; 通过反射解析类注解,判断该方法是否有注解,如果有则打印出注解的内容。
package com.company; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; public class Task10_02 { public static void main(String[] args) throws NoSuchFieldException { Class stuClass = Student.class; Field f = stuClass.getDeclaredField("name"); M m = f.getAnnotation(M.class); System.out.println(m.value()); } @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface M { String value() default "这是默认注释内容"; } public class Student{ @M public String name; } }
总结:
@Target(ElementType.TYPE) //接口、类、枚举
@Target(ElementType.FIELD) //字段、枚举的常量
练习3: 模仿上面的嵌套注解,除了可以字段类型的注解外,再加上字段长度和是否可以为空 的注解,并加入到@TableColumn 注解中去
package com.company; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public class Task10_03 { @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ColumnLength{ int value() default 0; } public @interface ColumnEmptyOrNot{ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ColumnLength{ boolean value() default true; } } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface TableColumn{ ColumnLength columnlength() default @ColumnLength; ColumnEmptyOrNot columnEmptyOrNot() default @ColumnEmptyOrNot; } }
练习4: 自定义嵌套注解,用来描述用户类中的「岗位」字段。
第一层注解定义岗位所在的部门
第二层注解定义岗位名称和描述。
//岗位名称 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PostName { String postName() default ""; } //岗位描述 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PostDescription { String postDescription() default ""; } //岗位所在部门 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Department { String departmentName() default ""; PostName postName() default @PostName; PostDescription postDescription() default @PostDescription; } public class Position { @Department(postName = @PostName , postDescription = @PostDescription) private String postName; private String postDescription; }
这篇关于Java基础知识-泛型、反射、注解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-06小米11i印度快充版ROM合集:极致体验,超越期待
- 2024-10-06【ROM下载】小米11i 5G 印度版系统, 疾速跃迁,定义新速度
- 2024-10-06【ROM下载】小米 11 青春活力版,青春无极限,活力全开
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求