18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)

简介: 18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)

18 反射reflect

18.1 类的加载

一、类对象


类的对象:基于某个类 new 出来的对象,也称为实例对象。

类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法) 。

注意:每个类加载到内存都会生成一个唯一的类对象。

在JVM中类的加载是有细分操作,当程序启动时候说那个到某个类,如果该类被加载到内存中,则JVM会通过三个步骤进行类的初始化操作【加载、连接和初始化】

18.2 Class对象

PS:在Java中提供一个类这个类叫做Class,用于存储类的字节码文件

对象【存储自定义类中描述信息】

因为JVM中是存在Class类对象的,所以Java就提供了另外一种方式,可以直接通过Class类的对象加载出来对应类的对象操作,这个操作就叫做“【反射reflect】”

例如创建Sutdent类

正常创建对象 Student stu = new Student();

但是在JVM中先得到Student类的.class字节码文件,通过字节码文件创建出Student类的对象

18.3 获取类对象的3种方法

  • 通过类的对象,获取类对象。
Student s = new Student();
Class c = s.getClass();
  • 通过类名获取类对象。
Class c = 类名.class;
  • 通过静态方法获取类对象。
Class c=Class.forName(“包名.类名”);

18.4 反射通用操作


18.4.1 常见方法
方法名 描述
public String getName() 获取类的完全名称
public Package getPackage() 获取包信息
public Class<? super T> getSuperclass() 获取父类
public Class<?>[] getInterfaces() 获取实现父接口
public Field[] getFields() 获取字段信息
public Method[] getMethods() 获取方法信息
public Constructor<?>[] getConstructors() 获取构造方法
public T newInstance() 反射创建对象
18.4.2 通用操作

反射通用操作:使用反射机制获取类对象,并使用Class对象的方法获取表示类成员的各种对象(比如Constructor、Method、Field等),实现反射各种应用。

案例演示:反射操作。

Person类:

public class Person implements Serializable,Cloneable{
  //姓名
  private String name;
  //年龄
  private int age;
  
  public Person() {
    System.out.println("无参构造执行了...");
  }
  
  public Person(String name, int age) {
    super();
    this.name = name;
    this.age = age;
    System.out.println("带参构造方法执行了...");
  }


  //吃
  public void eat() {
    System.out.println(name+"正在吃东西......");
  }


  @Override
  public String toString() {
    return "Person [name=" + name + ", age=" + age + "]";
  }
  //带参的方法
  public void eat(String food) {
    System.out.println(name+"开始吃...."+food);
  }
  
  //私有的方法
  private void privateMethod() {
    System.out.println("这是一个私有方法");
  }
  
  //静态方法
  public static void staticMethod() {
    System.out.println("这是一个静态方法");
  }
}

TestPerson类:

public class TestPerson {
  public static void main(String[] args) throws Exception {
    //调用测试以下方法
        //代码略
  }
  //获取类对象的三种方式
  public static void getClazz() throws Exception {
    //1使用对象获取类对象
    Person zhangsan=new Person();
    Class<?> class1=zhangsan.getClass();
    System.out.println(class1.hashCode());
    //2使用类名.class属性
    Class<?> class2=Person.class;
    System.out.println(class2.hashCode());
    //3使用Class的静态方法[推荐使用]
    Class<?> class3=Class.forName("com.qf.chap17_1.Person");
    System.out.println(class3.hashCode());
  }
  
  //1 使用反射获取类的名字、包名、父类、接口
  public static void reflectOpe1() throws Exception {
    //(1)获取类对象 Person
    Class<?> class1=Class.forName("com.qf.chap17_1.Person");
    //getName();
    System.out.println(class1.getName());
    //getPackage();
    System.out.println(class1.getPackage().getName());
    //getSuperClass();
    System.out.println(class1.getSuperclass().getName());
    //getInterfaces();
    Class<?>[] classes=class1.getInterfaces();
    System.out.println(Arrays.toString(classes));
    
    System.out.println(class1.getSimpleName());
    System.out.println(class1.getTypeName());
    
  }
  
  //2使用反射获取类的构造方法,创建对象
  public static void reflectOpe2() throws Exception{
    //(1)获取类的类对象
    Class<?> class1=Class.forName("com.qf.chap17_1.Person");
    //(2)获取类的构造方法 Constructor
    Constructor<?>[] cons=class1.getConstructors();
    for (Constructor<?> con : cons) {
      System.out.println(con.toString());
    }
    //(3)获取类中无参构造
    Constructor<?> con=class1.getConstructor();
    Person zhangsan=(Person)con.newInstance();
    Person lisi=(Person)con.newInstance();
    System.out.println(zhangsan.toString());
    System.out.println(lisi.toString());
    //简便方法:类对象.newInstance();
    Person wangwu=(Person)class1.newInstance();
    System.out.println(wangwu.toString());
    //(4)获取类中带参构造方法
    Constructor<?> con2=class1.getConstructor(String.class,int.class);
    Person xiaoli=(Person)con2.newInstance("晓丽",20);
    System.out.println(xiaoli.toString());
    
  }
  
  //3使用反射获取类中的方法,并调用方法
  public static void reflectOpe3() throws Exception{
    //(1)获取类对象
    Class<?> class1=Class.forName("com.qf.chap17_1.Person");
    //(2)获取方法  Method对象
    //2.1getMethods() 获取公开的方法,包括从父类继承的方法
    //Method[] methods=class1.getMethods();
    //2.2getDeclaredMethods() 获取类中的所有方法,包括私有、默认、保护的 、不包含继承的方法
    Method[] methods=class1.getDeclaredMethods();
    for (Method method : methods) {
      System.out.println(method.toString());
    }
    //(3)获取单个方法
    //3.1eat
    Method eatMethod=class1.getMethod("eat");
    //调用方法
    //正常调用方法  Person zhangsan=new Person();  zhangsan.eat();
    Person zhangsan=(Person)class1.newInstance();
    eatMethod.invoke(zhangsan);//zhangsan.eat();
    System.out.println("------------------");
    //3.2toString
    Method toStringMethod=class1.getMethod("toString");
    Object result=toStringMethod.invoke(zhangsan);
    System.out.println(result);
    System.out.println("-------------------");
    //3.3带参的eat 
    Method eatMethod2=class1.getMethod("eat", String.class);
    eatMethod2.invoke(zhangsan, "鸡腿");
    
    //3.4获取私有方法
    Method privateMethod=class1.getDeclaredMethod("privateMethod");
    //设置访问权限无效
    privateMethod.setAccessible(true);
    privateMethod.invoke(zhangsan);
    
    //3.4获取静态方法
    Method staticMethod=class1.getMethod("staticMethod");
    //正常调用 Person.staticMethod
    staticMethod.invoke(null);
    
  }
  
  //4使用反射实现一个可以调用任何对象方法的通用方法
  public static Object invokeAny(Object obj,String methodName,Class<?>[] types,Object...args) throws Exception {
    //1获取类对象
    Class<?> class1=obj.getClass();
    //2获取方法
    Method method=class1.getMethod(methodName, types);
    //3调用
    return method.invoke(obj, args);
  }
  
  //5使用反射获取类中的属性
  public static void reflectOpe4() throws Exception{
    //(1)获取类对象
    Class<?> class1=Class.forName("com.qf.chap17_1.Person");
    //(2)获取属性(字段) 公开的字段,父类继承的字段
    //Field[] fields=class1.getFields(); 
    //getDeclaredFields()获取所有的属性,包括私有,默认 ,包含,
    Field[] fields=class1.getDeclaredFields();
    System.out.println(fields.length);
    for (Field field : fields) {
      System.out.println(field.toString());
    }
    //(3)获取name属性
    Field namefield=class1.getDeclaredField("name");
    namefield.setAccessible(true);
    //(4)赋值  正常调用  Person zhangsan=new Person(); zhangsan.name="张三";
    Person zhangsan=(Person)class1.newInstance();
    namefield.set(zhangsan, "张三"); //zhangsan.name="张三";
    //(5) 获取值
    System.out.println(namefield.get(zhangsan));// zhangsan.name
  }
}

18.5 设计模式


18.5.1 概念
  • 一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
  • 可以简单理解为特定问题的固定解决方法。
  • 在Gof的《设计模式》书中描述了23 种设计模式。
8.5.2 好处

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、重用性。

18.5.3 工厂设计模式

开发中有一个非常重要的原则“开闭原则”,对拓展开放、对修改关闭。

工厂模式主要负责对象创建的问题。

可通过反射进行工厂模式的设计,完成动态的对象创建。

案例演示:

Usb类:

/**
 * 父类产品
 * @author wgy
 *
 */
public interface Usb {
  void service();
}

Mouse类:

public class Mouse implements Usb{

  @Override
  public void service() {
    System.out.println("鼠标开始工作了.....");
  }

}

Fan类:

public class Fan implements Usb{

  @Override
  public void service() {
    System.out.println("风扇开始工作了...");
  }

}

KeyBoard类:

public class KeyBoard implements Usb{

  @Override
  public void service() {
    System.out.println("键盘开始工作了...");
  }

}

UsbFactory类:

public class UsbFactory {
  public static Usb createUsb(String type) {//类型的全名称 com.qf.
    Usb usb=null;
    Class<?> class1=null;
    try {
      class1 = Class.forName(type);
      usb=(Usb)class1.newInstance();
    } catch (Exception e) {
      System.out.println(e.getMessage());
    }
    
    return usb;
  }
}

TestUsb测试类:

public class TestUsb {
  public static void main(String[] args) throws Exception{
    System.out.println("=========请选择 1 鼠标  2风扇 3 u盘===========");
    Scanner input=new Scanner(System.in);
    String choice=input.next();
    //1 = com.qf.chap17_2.Mouse
    //2 = com.qf.chap17_2.Fan
    //3 = com.qf.chap17_2.Upan
    //4 = com.qf.chap17_2.KeyBoard
    Properties properties=new Properties();
    FileInputStream fis=new FileInputStream("src\\usb.properties");
    properties.load(fis);
    fis.close();
    
    Usb usb=UsbFactory.createUsb(properties.getProperty(choice));
    if(usb!=null) {
      System.out.println("购买成功");
      usb.service();
    }else {
      System.out.println("购买失败,您要购买的产品不存在");
    }
  }
}

usb.properties配置文件。

1=com.qf.chap17_2.Mouse
2=com.qf.chap17_2.Fan
3=com.qf.chap17_2.Upan
4=com.qf.chap17_2.KeyBoard
18.5.4 单例模式

单例(Singleton):只允许创建一个该类的对象。

方式一:饿汉式(类加载时创建,天生线程安全)。

public class SingleTon {
  private static final SingleTon instance=new SingleTon();
  private SingleTon() {}
  public static SingleTon getInstance() {
    return instance;
  }
}

方式二:懒汉式(使用时创建,线程不安全,加同步)。

public class SingleTon2 {
  // 创建对象
  private static SingleTon2 instance = null;

  // 私有化构造方法
  private SingleTon2() {
  }

  // 静态方法
  public static  SingleTon2 getInstance() {
    if(instance==null) {//提高执行效率
      synchronized (SingleTon2.class) {
        if (instance == null) {
          instance = new SingleTon2();
        }
      }
    }
    return instance;
  }
}

方式三:懒汉式(静态内部类写法)

public class SingleTon3 {
  private SingleTon3() {}
  
  private static class Holder{
    static SingleTon3 instance=new SingleTon3();
  }
  
  public static SingleTon3 getInstance() {
    return Holder.instance;
  }
}

18.6 枚举


枚举是一个引用类型,枚举是一个规定了取值范围的数据类型。

  • 枚举变量不能使用其他的数据,只能使用枚举中常量赋值,提高程序安全性。

  • 定义枚举使用enum关键字。
  • 枚举的本质:
  • 枚举是一个终止类,并继承Enum抽象类。
  • 枚举中常量是当前类型的静态常量。

案例演示:

/**
 * 性别枚举
 */
public enum Gender {
  MALE,FEMALE;
}

注意:

18.7 注解


18.7.1 概念

注解(Annotation):是代码里的特殊标记, 程序可以读取注解,一般用于替代配置文件。

开发人员可以通过注解告诉类如何运行。

  • 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
18.7.2 定义注解

定义注解使用@interface关键字,注解中只能包含属性。

常见注解:@Override、@Deprecated

案例演示:

public @interface MyAnnotation {
  //属性(类似方法)
  String name() default "张三";
  int age() default 20;
  
}

18.7.3 注解属性类型
  • String类型
  • 基本数据类型
  • Class类型
  • 枚举类型
  • 注解类型
  • 以上类型的一维数组
18.7.4 元注解

元注解:用来描述注解的注解。

@Retention:用于指定注解可以保留的域。

  • RetentionPolicy.CLASS:
    注解记录在class文件中,运行Java程序时, JVM不会保留,此为默认值。
  • RetentionPolicy.RUNTIME:
    注解记录在 class文件中,运行Java程序时,JVM会保留,程序可以通过反射获取该注释
  • RetentionPolicy.SOURCE:
    编译时直接丢弃这种策略的注释。

@Target:

  • 指定注解用于修饰类的哪个成员。

案例演示:

PersonInfo注解类:

@Retention(value=RetentionPolicy.RUNTIME)
@Target(value= {ElementType.METHOD})
public @interface PersonInfo {
  String name();
  int age();
  String sex();
}

Person类:

public class Person {

  @MyAnnotation()
  public void show() {
    
  }
  
  //@MyAnnotation2(value="大肉",num=25)
  public void eat() {
    
  }
  
  @PersonInfo(name="小岳岳",age=30,sex="男")
  public void show(String name,int age,String sex) {
    System.out.println(name+"==="+age+"===="+sex);
  }
  
}

TestAnnotation类:

public class Demo {
  public static void main(String[] args) throws Exception{
    //(1)获取类对象
    Class<?> class1=Class.forName("com.qf.chap17_5.Person");
    //(2)获取方法
    Method method=class1.getMethod("show", String.class,int.class,String.class);
    //(3)获取方法上面的注解信息 personInfo=null
    PersonInfo personInfo=method.getAnnotation(PersonInfo.class);
    //(4)打印注解信息
    System.out.println(personInfo.name());
    System.out.println(personInfo.age());
    System.out.println(personInfo.sex());
    //(5)调用方法
    Person yueyue=(Person)class1.newInstance();
    method.invoke(yueyue, personInfo.name(),personInfo.age(),personInfo.sex());

  }
}


目录
相关文章
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
160 57
|
11天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
65 8
|
2月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
40 4
|
4天前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
|
2月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
4月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
2月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
2月前
|
设计模式 安全 Java
Kotlin - 改良设计模式 - 构建者模式
Kotlin - 改良设计模式 - 构建者模式