原文

概况

使用java的反射,可以让我们检查(或者修改)类,接口,字段,方法的特性。当你在编译期不知道他们的名字的时候非常有用。

除此之外,可以使用反射来创建实例,调用方法或者get/set 字段值。

设置项目

需要做的只有导个包。

import java.lang.reflect.*; //根据使用的情况导特定的,比如reflect.constructor等

简单例子

先加一下junit的依赖,然后添加一个Person类,两个字段。

public class Person {
private String name;
private int age;
}

写个测试方法来获取这个类的所有字段。

 /**
* 包含所有字段
*/
@Test
public void containAllFields() {
Object p = new Person();
Field[] declaredFields = p.getClass().getDeclaredFields();
List<String> actualFields = Arrays.stream(declaredFields).map(field -> field.getName()).collect(Collectors.toList());
List<String> exceptFields = new ArrayList<>();
exceptFields.add("age");
exceptFields.add("name");
Assert.assertTrue(exceptFields.containsAll(actualFields));
}

使用场景

最常用的使用场景为数据库表和实体类做字段映射。

检查java类

下面来做一些测试,用来获取前面提到过的比如类名,修饰符,字段,方法,实现的接口之类的东西。

准备工作

先创建一个Eating接口

public interface Eating {
String eats();
}

创建一个抽象Animal类来实现这个Eating接口

package com.lou.reflect.test;

public abstract class Animal implements Eating {
public static String CATEGORY = "domestic";
private String name; protected abstract String getSound(); public Animal(String name) {
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

创建一个Locomotion接口用来描述动物的行动方式。

package com.lou.reflect.test;

public interface Locomotion {
String getLocomotion();
}

创建一个具体的Goat类,继承自Animal同时实现Locomotion接口。

package com.lou.reflect.test;

public class Goat extends Animal implements Locomotion {
public Goat(String name) {
super(name);
} @Override
protected String getSound() {
return "山羊叫";
} @Override
public String eats() {
return "吃草";
} @Override
public String getLocomotion() {
return "行走";
}
}

创建一个Bird类,继承自Animal

public class Bird extends Animal {
private boolean walks; public Bird() {
super("鸟");
} public Bird(String name) {
super(name);
} public Bird(String name, boolean walks) {
super(name);
this.walks = walks;
} @Override
protected String getSound() {
return null;
} @Override
public String eats() {
return null;
} public boolean isWalks() {
return walks;
} public void setWalks(boolean walks) {
this.walks = walks;
}
}

做好准备工作之后开始下面的测试。

类名

获取类名。

/**
* simpleName为Goat,
* name为com.lou.reflect.test.Goat
* cannocalName为com.lou.reflect.test.Goat
*/
@Test
public void givenObjectThenGetNameTest() {
Object goat = new Goat("山羊");
Class<?> goatClazz = goat.getClass(); Assert.assertEquals("Goat", goatClazz.getSimpleName());
Assert.assertEquals("com.lou.reflect.test.Goat", goatClazz.getName());
Assert.assertEquals("com.lou.reflect.test.Goat",goatClazz.getCanonicalName());
}

类修饰符

/**
* 通过获取类的modifier来判断类的特性
* @throws Exception
*/
@Test
public void givenObjectThenGetModifiersTest() throws Exception {
Class<?> animalClazz = Class.forName("com.lou.reflect.test.Animal");
Class<?> goatClazz = Class.forName("com.lou.reflect.test.Goat");
//获取两个类的修饰符
int animalModifiers = animalClazz.getModifiers();
int goatModifiers = goatClazz.getModifiers();
//判断是否是公有
Assert.assertTrue(Modifier.isPublic(goatModifiers));
//判断是否是抽象
Assert.assertTrue(Modifier.isAbstract(animalModifiers));
//判断是否为公有
Assert.assertTrue(Modifier.isPublic(animalModifiers));
}

包信息

/**
* 获取包信息
*/
@Test
public void givenClassThenGetPackageNameTest() {
Goat goat = new Goat("山羊");
Package goatPackage = goat.getClass().getPackage();
Assert.assertEquals("com.lou.reflect.test", goatPackage.getName());
}

父类信息

/**
* 获取父类信息
*/
@Test
public void givenClassThenGetSupperClassTest() {
Goat goat = new Goat("山羊");
String str = "hello"; Class<?> goatSupperClazz = goat.getClass().getSuperclass();
Class<?> strSupperClazz = str.getClass().getSuperclass(); Assert.assertEquals("com.lou.reflect.test.Animal",goatSupperClazz.getName());
Assert.assertEquals("java.lang.Object",strSupperClazz.getName());
}

实现的接口

/**
* 获取实现的接口信息
*
* @throws Exception
*/
@Test
public void givenClassThenGetImpMethodsTest() throws Exception { Class<?> goatClazz = Class.forName("com.lou.reflect.test.Goat");
Class<?> animalClazz = Class.forName("com.lou.reflect.test.Animal");
Class<?>[] goatInterfaces = goatClazz.getInterfaces();
Class<?>[] animalInterfaces = animalClazz.getInterfaces(); //实现的接口,都是1个,虽然goat的父类实现了一个,goat自己也实现了一个。
//只能获取用implements显式实现的接口。如果要全部就只能递归了。
Assert.assertEquals(1, goatInterfaces.length);
Assert.assertEquals(1, animalInterfaces.length); Assert.assertEquals("Locomotion", goatInterfaces[0].getSimpleName());
Assert.assertEquals("Eating", animalInterfaces[0].getSimpleName()); }

构造函数,方法,字段

/**
* 构造函数,方法,字段
*/
@Test
public void givenClassThenGetConstructorMethodField() throws Exception {
Class<?> goatClazz = Class.forName("com.lou.reflect.test.Goat");
Class<?> animalClazz = Class.forName("com.lou.reflect.test.Animal");
Constructor<?>[] goatCtors = goatClazz.getConstructors();
Assert.assertEquals(1, goatCtors.length);
Field[] animalFields = animalClazz.getDeclaredFields();
//一个静态的CATEGORY,一个name,所以是2个。
Assert.assertEquals(2, animalFields.length);
//3个。这里获取到的是显式申明的方法,不包括那些从object继承下来的
Method[] animalMethods = animalClazz.getDeclaredMethods();
Assert.assertEquals(3, animalMethods.length);
//getName,setName,getSound,
List<String> methodNames = Arrays.stream(animalMethods).map(method -> method.getName()).collect(Collectors.toList());
Assert.assertTrue(methodNames.containsAll(Arrays.asList("getName", "setName", "getSound")));
}

获取构造函数然后动态调用

/**
* 获取bird的3个构造函数,然后分别调用~。
* @throws Exception
*/
@Test
public void getConstructorThenCreateInstance() throws Exception {
Class<?> birdClazz = Class.forName("com.lou.reflect.test.Bird");
//获取无参的构造函数
Constructor<?> birdCtro1 = birdClazz.getConstructor();
//获取有一个String类型的参数的构造函数
Constructor<?> birdCtro2 = birdClazz.getConstructor(String.class);
//获取有一个String类型,一个boolean类型参数的构造函数
Constructor<?> birdCtro3 = birdClazz.getConstructor(String.class, boolean.class);
//调用无参的
Bird bird1 = (Bird) birdCtro1.newInstance();
Assert.assertEquals("鸟", bird1.getName());
//调用有一个参数的
Bird bird2 = (Bird) birdCtro2.newInstance("bird2");
Assert.assertEquals("bird2", bird2.getName());
//调用两个参数的构造函数
Bird bird3 = (Bird) birdCtro3.newInstance("bird3", true);
Assert.assertEquals("bird3", bird3.getName());
Assert.assertEquals(true, bird3.isWalks());
}

运行期间修改field值,调用method

/**
* 通过获取name字段,然后动态修改。
* 通过动态调用setName方法,修改name的值
* @throws Exception
*/
@Test
public void givenClassThenModifyFields() throws Exception {
Class<?> birdClazz = Class.forName("com.lou.reflect.test.Bird");
Bird bird =(Bird) birdClazz.newInstance();
//定义在父类
Field nameField = birdClazz.getSuperclass().getDeclaredField("name");
//先设置为可访问
nameField.setAccessible(true);
nameField.set(bird,"一只bird");
Assert.assertEquals("一只bird",bird.getName());
Method setNameMethod = birdClazz.getSuperclass().getDeclaredMethod("setName", String.class);
setNameMethod.invoke(bird,"又一只");
Assert.assertEquals("又一只",bird.getName());
}

[java 基础]反射入门的更多相关文章

  1. Java基础语法入门01

    Java基础语法入门01 学习java你要先进行去了解JDK,JRE,JVM JDK Java开发工具包 JRE Java语言开发的运行环境 JVM Java虚拟机,用于Java语言的跨平台所用. 当 ...

  2. Java基础-反射(reflect)技术详解

    Java基础-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类加载器 1>.JVM 类加载机制  如下图所示,JVM类加载机制分为五个部分 ...

  3. Java基础——反射

    今天学到Java基础中的反反射.依照我学习后的个人理解呢,反射就是一套获取类.属性.方法等的工具吧.(其实,感觉学完反射后,有点像喝凉水,解渴但确实我也没体会出它有什么味道,我可能没有学到精髓吧.自己 ...

  4. Java基础反射(二)

    原文地址http://blog.csdn.net/sinat_38259539/article/details/71799078 反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Cla ...

  5. java基础(反射,注解,多线程,juc)

    JAVA基础 java反射 class对象 三种方式获取class加载时对象 1.class.forName("全类名"):将字节码文件加载进内存,返回class对象 2.类名.c ...

  6. java基础知识入门

    一.java简介及原理图 Java的前世今生 Java之父詹姆斯·高斯林: 1967年, 12岁用报废的电话机和电视做了一台电子游戏机; 1983年, 获得卡内基梅隆大学计算机科学博士学位; 1983 ...

  7. JAVA基础-反射机制

    什么是JAVA的反射机制 Java反射是Java被视为动态(或准动态)语言的一个关键性质.这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其 ...

  8. java基础-反射(细节)

    java面试题--java反射机制? Java反射机制的作用:1)在运行时判断任意一个对象所属的类.2)在运行时判断任意一个类所具有的成员变量和方法.3)在运行时任意调用一个对象的方法4)在运行时构造 ...

  9. Java基础—反射(转载)

    转载自: JAVA反射与注解 JAVA反射 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射机制是什么 反射 ...

随机推荐

  1. 探索Dynamics 365 用户能够登录使用的最小权限需求

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  2. 如何抓取 framework input 事件相关 log

    出现事件输入相关的问题时, 建议先 followhttp://429564140.iteye.com/blog/2355405来检测对应的设备是否有响应输入 如果没有响应输入,则可能是 driver ...

  3. Linux学习入门-------------------------VMvare与镜像的安装与配置

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qq_39038465/article/d ...

  4. 云K8S - AWS容器库ECR(ERS)编排ECS-EKS以及阿里云编排ACS-ACK

    云K8S相关 AWS 部分-ECR(ERS) ECS EKS 20180824 Chenxin AWS的容器编排目前分为 ECS 和 EKS 两种. AWS价格说明 Fargate模式的ECS,换算成 ...

  5. mysqld_safe error: log-error set to '/data/log/mysqld.log', however file don't exists. Create writable for user 'mysql'.The server quit without updating PID file (/data/mysql/mysqld.pid)

    [oot@cent65 bin]# service mysqld startStarting MySQL.2019-10-28T15:56:47.786960Z mysqld_safe error: ...

  6. SQL Server重建索引与重组索引会更新统计信息吗?

    在SQL Server中重建索引(Rebuild Index)与重组索引(Reorganize Index)会触发统计信息更新吗? 那么我们先来测试.验证一下: 我们以AdventureWorks20 ...

  7. 基于django中间件的编程思想

    目录 前言 前期准备 importlib模块介绍 基于django中间件的编程思想 django中settings源码 配置文件的插拔式设计 基于django中间件的思想,实现功能配置 前言 在学习d ...

  8. (day68)Vue-CLI项目、页面跳转和传参、生命周期钩子

    目录 一.Vue-CLI (一)环境搭建 (二)项目的创建 (三)项目目录结构 (四)Vue组件(.vue文件) (五)全局脚本文件main.js(项目入口) (六)Vue请求生命周期 二.页面跳转和 ...

  9. node-express脚手架生成的项目中实现浏览器缓存

    前言: 最近在做基于 node-express 的个人站点 朵朵视野 ,在站点发布之后自己在访问测试的过程中发现站点是没有缓存机制的,这样就导致每次访问站点都需要重新去加载资源,很消耗资源以及用户体验 ...

  10. linux-创建/使用快照/克隆(类似windows中备份还原)

    一. 创建/使用快照 1.什么是快照 ​ 说的直白一点,就是创建一个备份. ​ 当执行了不可逆的错误操作后,可以通过快照用来恢复系统 2.创建快照的3种模式 ​ 挂载状态下创建快照 ​ 开机状态下创建 ...