Java 反射之根基 Class 类

点击上方关注“追梦 Java”,一起追梦!

Java中反射机制很重要,Java的动态语言就是靠反射机制实现的,反射技术也是程序员走的更远必不可少的一个技能。一般情况下我们都是通过类来创建对象,如果要求通过一个对象找到一个类,就需要用到反射机制,各种开源框架的编写更是离不开反射的机制。

1

了解反射机制

1、一般我们写代码,按照如下的步骤使用一个类:

1、使用关键字 import 导入类所在的包;

2、使用类名称或者接口名称定义对象;

3、通过关键字 new 进行类对象实例化;

4、使用【对象.属性】进行类中属性的调用;

5、使用【对象.方法】进行类中方法的调用;

而 Java 中的反射过程就是指:在程序的运行过程中,把 Java 类中的各种元素映射成对应的类,并能动态的执行这些类的方法。

2、Java 反射机制里重要的几个类:

类:java.lang.Class

属性:java.lang.reflect.Field

构造方法:java.lang.reflect.Constructor

方法:java.lang.reflect.Method

用反射可以轻松的获取一个类中的以上元素,并可以动态地对其进行调用,在不知道别人写的代码的时候,可以通过配置来调用别人的代码,很多开源框架都离不开反射机制,反射的魅力也正如此。

2

认识 Class 类

1、了解 Object 类:

我们知道如果一个类没有明确的声明集成自哪个父类时,那么它默认继承 Object 类,Object 类是所有类的父类。

Object 类中的 getClass()方法的源码:

public final native Class<?> getClass();

getClass()方法返回的类型是一个 Class 类,Class 类就是 Java 反射的源头。以上的源码用到了 native 关键字和泛型,使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++ 语言实现的,由 Java 通过 JNI 去调用的本地接口;如果不让程序出现警告信息,可以在使用时指定其操作的泛型具体类型,或者直接用“?”来取代。

2、获取 Class 类的 4 种实例化方式:

1、通过类本身的 class 属性实例化【类.class】;

2、通过 Object 类的 getClass()方法实例化【对象.getClass()】;

3、通过【Class.forName()方法】实例化,这种方式最常用,体现出了反射的动态性;

4、通过类的加载器获取;

/**
 * 如何获取Class类的实例的4种方式
 */
@Test
  public void TestClass() throws Exception {
    // 1、调用运行时类本身的.class属性
    Class<?> clazz = User.class;
    System.out.println(clazz);


    // 2、通过运行时类的对象获取
    User p = new User();
    Class<?> clazz1 = p.getClass();
    System.out.println(clazz1);


    // 3、通过Class的静态方法获取,体现了反射的动态性,
    // 传入className就能创建Class类的实例
    String className = "com.jpm.reflection.User";
    Class<?> clazz3 = Class.forName(className);
    System.out.println(clazz3);


    // 4、通过类的加载器获取
    ClassLoader classLoader = this.getClass().getClassLoader();
    Class clazz4 = classLoader.loadClass(className);
    System.out.println(clazz4);
  }


/**
 * User类,用于演示反射,为了不影响体验,代码见文章末尾
 */

运行结果:

class com.jpm.reflection.User
class com.jpm.reflection.User
class com.jpm.reflection.User
class com.jpm.reflection.User

3

类的加载器 ClassLoader

1、了解 Class 类的 3 种加载器:

1、系统类加载器(AppClassLoader):加载自定义类和普通 jar 包;

2、扩展类加载器(ExtClassLoader):加载 jre/lib/ext 目录下的类;

3、启动类加载器(BootStrapClassLoader):加载 jdk 的核心类库,启动类加载器 Java 代码获取不到;

/**
   * 类的加载器测试
   */
  @Test
  public void testClassLoader() throws Exception {
    ClassLoader loader1 = this.getClass().getClassLoader();
    // 获取系统类加载器:sun.misc.Launcher$AppClassLoader@7852e922
    System.out.println(loader1);


    ClassLoader loader2 = loader1.getParent();
    // 获取系统类加载器的父类,扩展类加载器:sun.misc.Launcher$ExtClassLoader@330bedb4
    System.out.println(loader2);


    ClassLoader loader3 = loader2.getParent();
    // 获取扩展类的加载器的父类为null,说明引导类加载器无法在代码中获取
    System.out.println(loader3);


    Class clazz1 = User.class;
    ClassLoader loader4 = clazz1.getClassLoader();
    // 普通类用系统类加载器加载:sun.misc.Launcher$AppClassLoader@7852e922
    System.out.println(loader4);


    Class clazz2 = Class.forName("java.lang.String");
    ClassLoader loader5 = clazz2.getClassLoader();
    // String类是jdk的核心类库,用引导类加载器加载,返回null,说明类加载器无法在代码中获取
    System.out.println(loader5);
  }

运行结果:

sun.misc.Launcher$AppClassLoader@7852e922
sun.misc.Launcher$ExtClassLoader@330bedb4
null
sun.misc.Launcher$AppClassLoader@7852e922
null

2、类加载器的应用案例:获取 Java 包里的配置文件

@Test
  public void test() throws IOException {
    // 类加载器的应用,获取java包里的配置文件,但是不能获取工程目录下的文件
    ClassLoader loader = this.getClass().getClassLoader();
    InputStream is = loader.getResourceAsStream("configs//jdbc.properties");
    Properties properties = new Properties();
    properties.load(is);
    is.close();
    String user = properties.getProperty("user");
    System.out.println(user);
    String pwd = properties.getProperty("pwd");
    System.out.println(pwd);


    // 文件名不一定是properties后缀,也可以是txt等
    InputStream is1 = loader.getResourceAsStream("configs//jdbc.txt");
    Properties properties1 = new Properties();
    properties1.load(is1);
    is1.close();
    String user1 = properties1.getProperty("user");
    System.out.println(user1);
    String pwd1 = properties1.getProperty("pwd");
    System.out.println(pwd1);
  }

配置文件内容:

运行结果:

properties
jdbc.properties
txt
jdbc.txt

2、获取工程下的配置文件

上面的类加载器获取 java 包里的配置文件,但是不能获取工程目录下的文件,要想获取工程目录下的文件,可以使用如下的代码:

@Test
  public void test2() throws IOException {
    // 工程下的文件获取,文件名不一定是properties后缀,也可以是txt等
    InputStream is = new BufferedInputStream(new FileInputStream(new File("jdbc2.properties")));
    Properties pro = new Properties();
    pro.load(is);
    is.close();
    System.out.println(pro.getProperty("user"));
    System.out.println(pro.getProperty("pwd"));
  }

运行结果:

root
root123

4

Java 反射初体验

下面一个例子用来演示如何通过 Java 的反射机制,动态创建 User 类对象,并调用 User 类对象的属性赋值以及调用它的方法。

  /**
   * 反射的初步体验
   */
  @Test
  public void TestReflection() throws Exception {
    Class<?> clazz = Class.forName("com.jpm.reflection.User");
    // 创建运行时类User的对象
    Object user = clazz.newInstance();
    System.out.println(user);


    // 通过反射调用运行时类的指定public属性
    Field f1 = clazz.getField("name");
    f1.set(user, "JPM");
    System.out.println(user);


    // 通过反射调用运行时类的指定private属性
    Field f2 = clazz.getDeclaredField("age");
    f2.setAccessible(true); // 为私有属性赋值
    f2.set(user, 18);
    System.out.println(user);


    // 通过反射调用运行时类的指定方法
    Method m1 = clazz.getMethod("show");
    m1.invoke(user);


    Method m2 = clazz.getMethod("display", String.class);
    m2.invoke(user, "BJ");
  }
  
/**
 * User类,用于演示反射
 */
public class User {


  public String name;
  private int age;


  // 创建类时尽量写一个空参的构造器
  public User() {
    super();
  }


  public User(String name) {
    super();
    this.name = name;
  }


  public User(String name, int age) {
    super();
    this.name = name;
    this.age = age;
  }


  public String getName() {
    return name;
  }


  public void setName(String name) {
    this.name = name;
  }


  public int getAge() {
    return age;
  }


  public void setAge(int age) {
    this.age = age;
  }


  @Override
  public String toString() {
    return "User [name=" + name + ", age=" + age + "]";
  }


  public void show() {
    System.out.println("show method run!");
  }


  public void display(String address) {
    System.out.println("display method run,address=" + address);
  }
}

运行结果:

User [name=null, age=0]
User [name=JPM, age=0]
User [name=JPM, age=18]
show method run!
display method run,address=BJ

本文主要介绍了反射的概念、反射的源头 Class 类、类的 3 种加载器、类加载器ClassLoader 的应用场景、用Java 反射获取用户自定义类User,初步体验了 Java 反射机制的魅力。

后面我会用 2 篇文章来说明 Java 反射机制的深入体验,反射的高级应用动态代理的实现,敬请期待......

追梦Java

知识指导行动,行动决定命运。

长按二维码关注追梦Java

1、入职新公司,如何快速凸显个人价值

2、Java 开发分享

3、Java 开发经验分享

4、Java面试之 volatile 和 synchronized 及 Lock 的区别

5、关于多线程创建的几个问题

6、关于多线程共享资源的几个安全性问题

7、关于多线程操作的几个方法

8、Java中的死锁问题

9、带你认识 File 类

10、IO 流,掌控一切

11、不能不懂的 IO 处理流

12、IO 操作大结局

有用的话点个在

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值