第一章 反射
1 反射介绍
1.1 理解
- 反射 : 是java的一个非常突出的动态机制:Reflection
- 反射是发生在程序运行期间的
1.2 重难点
- 反射整体了解
- 创建对象重要
1.3 实现功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理
1.4 反射的源头
-
Class类型的对象:在一个类型加载到内存中就存在一个属于表示当前类型的Class对象
唯一的,不变的
- Class:类类实例表示正在运行的Java应用程序中的类和接口。
1.5 代码
classname.properties
className=com.whl.reflect.Person
className1=com.whltest.reflect.Teacher
reflect
public class Class001_Reflect {
public static void main(String[] args) throws Exception {
//jdk11中允许声明局部变量通过var关键字
//申明的同时必须赋值,决定变量的类型
Properties pro = new Properties();
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("classname.properties"));
Person p = (Person) Class.forName(pro.getProperty("className1")).newInstance();
p.sleep();
}
}
class Person{
void sleep(){
System.out.println("休息");
}
}
class Student extends Person{
void sleep(){
System.out.println("一边听课一边休息");
}
}
class Teacher extends Person{
void sleep(){
System.out.println("一边教课一边休息");
}
}
2 反射源头的获取
2.1 获取的方式
-
反射的源头 :
Class对象
1.类名.class
2.Class.forName(权限定名) 权限定名: 包名.类名 --> 推荐
3.对象.getClass() -
获取基本数据类型的Class对象
- int.class
- Integer.TYPE
2.2 代码
public class Class002_Reflect {
public static void main(String[] args) throws ClassNotFoundException {
//1.类名.class
Class<String> cls1 = String.class;
System.out.println(cls1.toString());
//2.Class.forName(权限定名)
Class cls2 = Class.forName("java.lang.String");
System.out.println(cls2);
System.out.println(cls1==cls2);
//3.对象.getClass()
Class cls3 = "".getClass();
System.out.println(cls3);
//获取当前Class对象所表示类型的父类的Class对象
Class cls4 = cls1.getSuperclass();
System.out.println(cls4);
System.out.println(cls4==Object.class);
//获取基本数据类型的Class对象
System.out.println(int.class);
System.out.println(Integer.class);
System.out.println(int.class==Integer.TYPE);
System.out.println(Double.TYPE);
}
}
3 通过反射创建对象
3.1 两种方法
- Class ->newInstance() 默认调用空构造为对象初始化信息–>不推荐使用
- Constructor->newInstance() 创建对象
- 获取某一个类中构造器
构造器 getConstructor(类<?>… parameterTypes) 返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数。
构造器<?>[] getConstructors() 返回一个包含 构造器对象的数组, 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
构造器 getDeclaredConstructor(类<?>… parameterTypes) 返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
构造器<?>[] getDeclaredConstructors() 返回 构造器对象的数组, 构造器对象反映由此 类对象表示的类声明的所有构造函数。 - 调用Constructor类提供的newInstance方法,创建对象的时候调用当前构造器初始化信息
- 获取某一个类中构造器
3.2 代码
public class Class003_Constructor {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
//1.创建对象
//Employee.class.newInstance();
System.out.println(Employee.class.newInstance());
//获取构造器
Constructor[] cons = Employee.class.getConstructors();
System.out.println(Arrays.toString(cons));
System.out.println("---------------");
Constructor<Employee> con = Employee.class.getDeclaredConstructor(int.class,String.class);
System.out.println(con);
//2.创建 对象
//私有的构造器先忽略权限再次使用
con.setAccessible(true);
Employee emp = con.newInstance(01, "sjdfkl");
System.out.println(emp);
}
}
员工类
public class Employee{
private int no;
private String name;
private double sal;
public int i = 1;
public static int j = 2;
public Employee() {
}
private Employee(int no, String name) {
this.no = no;
this.name = name;
}
public Employee(int no, String name, double sal) {
this.no = no;
this.name = name;
this.sal = sal;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
@Override
public String toString() {
return "Employee{" +
"no=" + no +
", name='" + name + '\'' +
", sal=" + sal +
'}';
}
//静态方法
public static boolean testStatic(int i){
System.out.println("静态方法testStatic");
return i>=0;
}
//私有方法
private void testPrivate(){
System.out.println("testPrivate");
}
}
4 通过反射操作成员
4.1 操作属性
- 获取属性
字段 getField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
字段[] getFields() 返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
字段 getDeclaredField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
字段[] getDeclaredFields() 返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。 - 获取属性值
Object get(Object obj) 返回指定对象上此 字段表示的字段的值。 - 设置属性值
void set(Object obj, Object value) 将指定对象参数上此 字段对象表示的字段设置为指定的新值。
4.2 操作方法
- 获取方法
方法 getMethod(String name, 类<?>… parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
方法[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
方法 getDeclaredMethod(String name, 类<?>… parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。 - 调用方法
Object invoke(Object obj, Object… args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。 - 忽略权限
- setAccessible(true)
4.3 代码
public class Class004_Member {
public static void main(String[] args) throws Exception {
Employee emp = new Employee();
testFiled(Employee.class);
testFiled1(Employee.class,emp);
testMethod(Employee.class,emp);
}
/*
操作方法
1.获取方法
方法 getMethod(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
方法[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
方法 getDeclaredMethod(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。
2.调用方法
Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。
*/
public static void testMethod(Class<Employee> cls,Employee emp) throws Exception {
//获取所有公共的方法
Method[] arr = cls.getMethods();
Arrays.stream(arr).forEach(System.out::println);
//获取指定的方法
Method method = cls.getDeclaredMethod("testPrivate");
System.out.println(method);
//调用方法
method.setAccessible(true);
System.out.println(method.invoke(emp));
//静态方法 存在返回值 可以跟随对象调用|可以跟随类名调用
for(Method m:arr){
if("testStatic".equals(m.getName())){
//System.out.println(m.invoke(emp,-1000));
System.out.println(m.invoke(null,1000));
}
}
}
/*
操作属性
1.获取属性
字段 getField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
字段[] getFields() 返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
字段 getDeclaredField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
字段[] getDeclaredFields() 返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。
2.获取属性值
Object get(Object obj) 返回指定对象上此 字段表示的字段的值。
3.设置属性值
void set(Object obj, Object value) 将指定对象参数上此 字段对象表示的字段设置为指定的新值。
*/
public static void testFiled(Class<Employee> cls) throws NoSuchFieldException {
//获取所有公共的
Field[] fields = cls.getFields();
System.out.println(Arrays.toString(fields));
}
public static void testFiled1(Class<Employee> cls,Employee emp) throws NoSuchFieldException, IllegalAccessException {
//根据属性名获取指定的属性
Field cls1 = cls.getDeclaredField("name");
System.out.println(cls1);
/* Field[] cls2 = cls.getDeclaredFields();
System.out.println(Arrays.toString(cls2));*/
//忽略权限
cls1.setAccessible(true);
cls1.set(emp,"lisf");
System.out.println(cls1.get(emp));
}
}
第二章 注解
1 注解介绍
1.1 理解
- 注解 : 标注 --> 了解
jdk1.5新特性 - 理解为主,重点为注解的使用,注解解析器了解即可
1.2 作用
- 注释,解释的作用
- 标志检查的作用
- 添加注解的时候可以通过配置传递参数,运行期间通过反射获取到配置的数据,程序中进行使用
- 注解可以存在于Class文件中
- 注解大量的代替了配置文件的存在
1.3 语法
- @注解名(实参)
1.4 位置
- 任意位置
1.5 分类
-
jdk内置注解
@Override 限定重写父类方法
@Deprecated 标记已过时
@SuppressWarnings 抑制警告
@FunctionalInterface 函数式接口 -
元注解 : 注解的注解
@Target 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有)
@Documented 表示使用该注解的元素应被javadoc或类似工具文档化
@Inherited 表示一个注解类型会被自动继承, -
自定义注解
- 参数个数划分:
标记注解 : 没有参数
单值注解 : 一个参数
完整注解 : 多个参数
- 参数个数划分:
2 jdk内置注解
2.1 内置注解
- @Override 限定重写父类方法
- @Deprecated 标记已过时
- @SuppressWarnings 抑制警告
- @FunctionalInterface 函数式接口
2.2 代码
public class Class001_Annotation {
public static void main(String[] args) {
@SuppressWarnings("all")
ArrayList objects = new ArrayList();
test();
}
@Deprecated
static void test(){
List list2 = new ArrayList();
}
@Override
public String toString() {
return super.toString();
}
}
3 元注解
3.1 元注解
- @Target 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- @Retention 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有)
- @Documented 表示使用该注解的元素应被javadoc或类似工具文档化
- @Inherited 表示一个注解类型会被自动继承,
3.2 使用
public class Class002_Annotation {
@HaHa
int i = 1;
@HaHa
public static void main(String[] args) {
}
@Override
public String toString() {
return "Class002_Annotation{" +
"i=" + i +
'}';
}
}
@Inherited
@Documented
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface HaHa{}
4 自定义注解
4.1 自定义注解
- 通过@interface自定义注解类型
- 自定义的注解类型默认实现java.lang.annotation Annotation接口
- 自定义的注解类型不能显示的继承其他父类,实现其他接口
- 注解类中可以定义注解类型的字段,特殊的定义方法,需要字段名后面添加() --> 修饰符 类型 字段名();
- 如果注解类型中只有一个字段,建议这个字段名定义为value,赋值的时候可以直接赋值
- 注解类型中的字段的数据类型,只能为以下几种类型 : 基本数据类型,String,Annotation,枚举类型,或者这几种类型的数组
- 字段的修饰符只能为public|default
- 通过default关键字为字段定义默认值
4.2 代码
public class Class003_Defined {
@MyAnnotation("zhangsan")
String name;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String value();
int j() default -1;
}
5 水果清单实例
5.1 题目
- 水果清单 : 每天上货的清单
水果类型,水果数量
有些水果存在默认上货数量
有些水果不存在默认的上货数量 - 注意: 如果想要通过反射操作的注解类型,需要定义注解类型的声明周期为运行期
5.2 代码
public class Class004_FruitList {
public static void main(String[] args) throws Exception {
FruitList list = new FruitList();
list.pear = 15;
System.out.println(list);
testFruitList(list,FruitList.class);
System.out.println(list);
}
//注解解析器 : 获取当前对象的每一个属性,判断属性是否存在指定的上货数量,如果没有存在,进一步判断是否存在默认的上货数量,存在获取数量进行复制,不存在不上货
public static FruitList testFruitList(FruitList list,Class<FruitList> cls) throws Exception {
//判断是否存在水果清单的实例
if(list==null){
throw new NullPointerException("哪有清单!!!!");
}
//获取水果清单类型中的所有属性
Field[] fields = cls.getDeclaredFields();
//遍历每一个属性
for(Field field:fields){
//判断list清单中当前属性是否已经赋值,已经赋值就按照当前已经赋予的值上货
if(field.get(list)==null){
//没有赋值,判断当前属性上是否存在Num注解,如果不存在证明没有默认值
if(field.isAnnotationPresent(Num.class)){
//如果存在,获取默认值,赋值给list对象当前属性
Num num = field.getAnnotation(Num.class);
field.set(list,num.value());
}
}
}
return list;
}
}
//水果清单 :类型
class FruitList{
@Num(20)
Integer apple;
@Num
Integer pear;
Integer banana;
public FruitList() {
}
public FruitList(Integer apple, Integer pair, Integer banana) {
this.apple = apple;
this.pair = pair;
this.banana = banana;
}
@Override
public String toString() {
return "FruitList{" +
"apple=" + apple +
", pair=" + pair +
", banana=" + banana +
'}';
}
}
//注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Num{
int value() default 10;
}