反射
一.定义
- 反射是指在程序运行期间,能够观察和修改类或者类的对象的属性和行为的特性
项目开发中常见的使用反射的场景
- 使用JDBC连接数据库
- Servlet在Web容器中的加载和运行
二.作用
Java反射机制提供了以下的功能
- 在运行时获取类的修饰符,包名,类名,实现的接口,继承的父类
- 在运行时获取类的所有属性名,修饰符,属性类型
- 在运行时获取所有方法,方法的返回值类型,方法名,方法参数数量,方法参数类型
- 在运行时调用加载类的方法
三.常用API
Java反射API常用的类
- java.lang.Class
- java.lang.reflect.Method
- java.lang.Constructor
- java.lang.Field
- java.lang.Modifier
四.获取class对象的方式
public class TestClass {
public static void main(String[] args) throws Exception{
//第一种获取class对象的方式(常用方法)
//Class.forName(),括号内是字符串,表示想要获取类的全类名
//比如我想要获取当前类,获取的是当前类的class类型
Class<?> tClass = Class.forName("cn.kgc.kb11.TestClass");
//第二种获取class对象的方式
//用对象获取类型
//建一个对象,用对象.getClass()来获取
TestClass tc=new TestClass();
Class<? extends TestClass> tcClass = tc.getClass();
//第三种获取class对象的方式
//直接使用类型来获取class对象
Class<TestClass> typeClass = TestClass.class;
}
}
五.获取class对象的构造方法
Student类
放在cn.kgc.kb11包中
public class Student {
public int id;
String name;
private String gender;
private Student() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String getGender() {
return gender;
}
private void setGender(String gender) {
this.gender = gender;
}
}
测试类
放在cn.kgc.kb11.ref包中
public class TestStudent {
public static void main(String[] args) throws Exception{
//使用反射获取某个类的属性和方法以及构造方法
//1、获取Class对象,直接使用类型来获取
Class<Student> c = Student.class;
//获取包名
System.out.println(c.getPackage().getName());
//获取全类名
System.out.println(c.getName());
//获取构造方法
//当构造方法为公共的时候,可以用c.getConstructor()来获取
//当构造方法为私有的时候,用c.getDeclaredConstructor()来获取
Constructor<Student> cons = c.getDeclaredConstructor();
//虽然获取到构造方法了,但没有权限,还是私有的
// 这时候需要用setAccessible(true)来强行破解
cons.setAccessible(true);
//再用newInstance方法就可以获取到Student类型的对象了
Student s = cons.newInstance();
//打印一下,结果就是Student类型的地址
System.out.println(s);
}
}
运行结果
cn.kgc.kb11
cn.kgc.kb11.Student
cn.kgc.kb11.Student@1b6d3586
注:一些公司为了安全,采用枚举的方法,枚举可以防反射
六.获取属性以及给属性赋值
Student类
放在cn.kgc.kb11包中
public class Student {
public int id;
String name;
private String gender;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
'}';
}
private Student() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String getGender() {
return gender;
}
private void setGender(String gender) {
this.gender = gender;
}
}
测试类
放在cn.kgc.kb11.ref包中
public class TestStudent {
public static Student getStudent()throws Exception{
//把获取类的代码放进一个方法里,便于后续操作
Class<Student> c = Student.class;
Constructor<Student> cons = c.getDeclaredConstructor();
cons.setAccessible(true);
Student s = cons.newInstance();
return s;
}
public static void main(String[] args) throws Exception{
//1.创建对象,调用之前写好的方法
Student s = getStudent();
//获取对象
Class<? extends Student> aClass = s.getClass();
//获取属性
Field id = aClass.getField("id");
//给属性赋值,用set(赋值到哪个对象里,赋值的值是多少)来进行赋值
id.set(s,3);
System.out.println(s);
//但无法给私有属性赋值,编译不会出错,但运行是会出错
//要想给私有属性赋值,用如下的方法
Field df = aClass.getDeclaredField("gender");
df.setAccessible(true);
df.set(s,"男");
System.out.println(s);
//第二种给属性赋值的方法,获取属性数组
Field[] dfs = aClass.getDeclaredFields();
//然后遍历数组来给每个属性进行赋值
for (int i = 0; i < dfs.length; i++) {
//前提先破解
dfs[i].setAccessible(true);
if (null!=dfs[i].getName()&&"id".equals(dfs[i].getName())) {
dfs[i].set(s,6);
}else if (null!=dfs[i].getName()&&"name".equals(dfs[i].getName())){
dfs[i].set(s,"小宇");
}else if (null!=dfs[i].getName()&&"gender".equals(dfs[i].getName())){
dfs[i].set(s,"女");
}
}
System.out.println(s);
}
}
运行结果
Student{id=3, name='null', gender='null'}
Student{id=3, name='null', gender='男'}
Student{id=6, name='小宇', gender='女'}
七.获取方法
Student类
放在cn.kgc.kb11包中
public class Student extends Parent{
public int id;
String name;
private String gender;
private String show(int cnt,String good){
return "我叫"+name+",我的学号是:"+id+",我的性别是:"+gender
+",老师叫我拿"+cnt+"杯"+good+"来给大家";
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
'}';
}
private Student() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String getGender() {
return gender;
}
private void setGender(String gender) {
this.gender = gender;
}
}
父类Parent
放在cn.kgc.kb11包中
public class Parent {
private double money;
}
测试类
放在cn.kgc.kb11.ref包中
public class TestStudent {
public static Student getStudent()throws Exception{
//把获取类的代码放进一个方法里,便于后续操作
Class<Student> c = Student.class;
Constructor<Student> cons = c.getDeclaredConstructor();
cons.setAccessible(true);
Student s = cons.newInstance();
return s;
}
public static void main(String[] args) throws Exception{
Student s = getStudent();
Class<? extends Student> aClass = s.getClass();
//获取方法(私有方法的获取就不用多说了,参照私有属性的获取)
//getDeclaredMethod(方法名,参数类型的反射方式)
Method gender = aClass.getDeclaredMethod("setGender", String.class);
gender.setAccessible(true);
//方法用invoke进行传参
gender.invoke(s,"女");
//两个公共方法就正常传参就行
s.setId(1);
s.setName("鱼子酱");
System.out.println(s);
//多参数类型私有方法的传参
Method show = aClass.getDeclaredMethod("show", int.class, String.class);
show.setAccessible(true);
Object o = show.invoke(s, 25, "卡布奇诺");
System.out.println(o);
//获取父类的类型名
Type dad = aClass.getGenericSuperclass();
System.out.println(dad.getTypeName());
//获取父类的类型
Class<?> superclass = aClass.getSuperclass();
System.out.println(superclass);
}
}
运行结果
Student{id=1, name='鱼子酱', gender='女'}
我叫鱼子酱,我的学号是:1,我的性别是:女,老师叫我拿25杯卡布奇诺来给大家
cn.kgc.kb11.Parent
class cn.kgc.kb11.Parent
八.总结
- Java反射机制是Java语言被视为准动态语言的关键特性
- Java反射机制有优点也有缺点
- 一般用于构建框架