1、泛型
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
(1)、企业常用
a、总接口定义,以后定义的接口全都继承该接口,E为类型,PK为主键
public interface GenericService <E,PK>{
void save(E e);
void findById(PK id);
}
b、子接口定义,继承的时候直接写想应用的泛型
public interface EmployeeService extends GenericService <Employee,Integer>{
}
c、子接口实现类定义,需要实现总接口的抽象方法
public class EmployeeServiceImpl implements EmployeeService {
@Override
public void save(Employee e) {
}
@Override
public void findById(Integer id) {
}
}
(2)、集合中常用(List、Map),约束存储的类型
List<Employee> list =new ArrayList<>();//约束list中只能存储Employee对象
list.add(new Employee());
list.add(new Employee());
//list.add(new Department());//类型错误
(3)、定义返回结果集对象时也常用泛型
public class GenericDataResponse<T> { //下次实例化时候直接带上泛型说明返回的结果集对象
private Integer code;
private String msg;
private T data;
}
(4)、使用泛型的好处
a、消除ClassCastExcepion异常
b、类型参数化
c、编译期类型安全检查
d、减少代码中强制类型转换
2、反射
反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为。
当类加载以后,JVM 便自动产生一个Class 对象,使用该对象就可以获取一个类中的方法、成员以及构造方法的声明和定义等信息
(1)、获取Class对象
Student stu = new Student();//假设stu中已有初始值
三种方式a、Class clz = stu.getClass();
b、Class clz = com.demo.generic.Student.class;//类的全路径
c、Class clz = Class.forName("com.demo.generic.Student");//权限定名
(2)、根据类名找到想要执行的方法或者想要操作的属性,或者查找构造方法
a、获取方法 Method method = clz.getDeclaredMethod("getStudentName");//参数是方法名
注意:上述带不带Declared是有区别的, getDeclaredMethod获取的是自身声明的所有方法,包括public、protected、private方法,getMethod获取的是所有的共有方法,包括自身声明的public方法,从基类继承的public方法和接口实现的所有的public方法。
b、获取属性 Field field = clz.getDeclaredField("studentName");//参数是实体类中属性
c、获取构造方法
Constructor[] cs1s = clz.getConstructors();//获取所有的构造器(所有能访问到的,public)
Constructor[] cs2s = clz.getDeclaredConstructors();//获取所有声明的构造器
(3)、调用执行,方式是获得的Method对象调用invoke,所传递的参数是该方法所属的类的对象,返回值存到相应变量中
a、操作方法 String studentName = (String)method.invoke(student);//注意强转
b、操作属性 和操作方法不同,若是私有属性需要打开权限(此处假设我要修改该学生姓名)
field.setAccessible(true);//首先需要打开权限
field.set(student,"林黛玉")//操作该属性的set方法修改其值,第一个参数是该属性所属类的对象,第二个是set方法所需要的参数
(4)、输出相应变量
上述的例如操作方法所得到的String studentName可以直接输出
操作属性的set方法所得到的结果需要操作对象调用间接输出
3、注解(Annotation)
(1)、什么是注解?
JAVA注解用于为JAVA提供元数据。作为元数据,注解不直接影响你的代码执行。
(2)、注解有什么作用?
a、编写文档:通过代码里标识的元数据生成文档;
b、代码分析:通过代码里标识的元数据对代码进行分析;
c、编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查;
(3)、注解按功能或用途分类
a、系统内置注解:系统自带的注解类型,如@Override;
b、元注解:注解的注解,负责注解其他注解,如@Tagget;
c、自定义注解:用户根据需求自己定义的注解类型;
(4)、常用元注解-->四个
a、@Target 描述该注解修饰的范围,例如作用于方法或者属性
取值(ElementType):-->
FIELD:用于描述域,也就是属性(常用)
METHOD:用于描述方法(常用)
PARAMETER:用于描述参数
CONSTRUCTOR:用于描述构造器
PACKAGE:用于描述包
LOCAL_VARIABLE:用于描述局部变量
TYPE:用于描述类、接口(包括注解类型) 或enum声明
b、@Retention 描述该注解的生命周期,表示在什么编译级别上保存该注解的信息。
取值(RetentionPoicy):-->一般取值为RUNTIME
RUNTIME:在运行时有效(即运行时保留)
CLASS:在class文件中有效(即class保留)
SOURCE:在源文件中有效(即源文件保留)
c、@Documented 在生成javadoc文档的时候将该Annotation也写入到文档中
d、@Inherited 该注解被子类继承
(5)、以上课讲的自定义注解@CheckAge为例,源代码如下
a、首先假设你在实体类Employee中有这样一段代码,你想验证当这个实体类创建实例的时 候,所用的年龄是否符合我们所期望的年龄范围。
@CheckAge(max=50,min=18,message ="年龄不符合")
private Integer age;
b、建立.annotation文件,解释该注解,源代码如下
import java.lang.annotation.*;
@Target(ElementType.FIELD)//目标对象是类型、字段
@Retention(RetentionPolicy.RUNTIME)//保存至运行时
@Documented//生成javadoc文档时,该注解内容一起生成文档
@Inherited//该注解被子类继承
public @interface CheckAge
{ //该类中有什么取决于你注解上写了什么
int max() default 0;
int min() default 0;
String message() default "";
}
c、Main方法中应用
import com.demo.generic.Employee;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args)
throws NoSuchFieldException, IllegalAccessException {
Employee e =newEmployee(1L,"emp");
e.setAge(100);
Class clz = e.getClass();//反射
Field age = clz.getDeclaredField("age");//获取到age属性
age.setAccessible(true);//打开私有权限
//boolean hasCheck = age.isAnnotationPresent(CheckAge.class);判断是否有注解,省略...
//如果有,可以根据上述布尔属性做一个if判断
Integer ageValue = (Integer) age.get(e);//获取到真是age的值
//获取注解
CheckAge ageAn = age.getAnnotation(CheckAge.class);
int max = ageAn.max();//约定全局变量用包装类,局部变量用基本数据类型
int min = ageAn.min();
String msg = ageAn.message();
//判断当前实例age值在min-max之间
if(ageValue > max || ageValue < min) {
System.out.println(msg);
}else{
System.out.println("年龄合法");
}
}
}