反射
反射的概念及演示:
- 反射:又被称为“反向加载”。(之前创建某个类的对象时:Student stu = new Student());//正向加载—缺点:使当前类与Student类产生了依赖。
- 反射不直接使用“类名”,可以通过一个字符串描述的一个类名(可以将其记录在配置文件中),去加载这个类,并创建它的对象,进而去调用它的一些属性和方法。
- 反射的好处:解开类和类之间的“耦合”
代码图解:
运行时class文件的加载:
获取Class对象的三种方式:
-
Object类的getClass()方法:此方法会被任何类继承。
Student student = new Student();//创建Class对象
Class c1 = student.getClass();//获取 -
任何的数据类型(包括基本数据类型)都有一个静态的成员属性:Class
Class c2 = student.class;//注意:必须通过“类名”调用,不能通过“对象”调用。 -
调用Class类的静态方法:forName(“全类名”);[常用]
Class c3 = Class.forName(“day26作业代码.test01.Student”) ;
注意:以上三种方式都是:如果有Class对象,就直接获取。如果没有Class对象,会创建Class对象,然后再获取。
代码演示:
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException {
//获取Class对象的第一种方式:
Student student = new Student();
Class c1 = student.getClass();
System.out.println("第一种方式:"+c1.getMethods());
//第二种方式:
Class c2 = Student.class;
System.out.println("第二种方式:"+c2.getMethods());
//第三种方式:
Class c3 = Class.forName("day26作业代码.test02.Student");
System.out.println("第三种方式:"+c3.getMethods());
}
}
获取构造方法并创建对象-Constructor
作用:可以通过获取“Constructor对象”,来调用某个构造方法,去创建它的对象。
- 批量获取:
- public Constructor[] getConstructors();//获取所有的“公有构造方法”。
- public Constructor[] getDeclaredConstructors();//获取所有的构造方法,包括:公有,受保护,默认,私有
- 获取单个:
- public Constructor getConstructor(Class … params);//获取“某个”,“公有”的构造方法。
- public Constructor getDeclaredConstructor(Class … params);//获取某个构造方法,可以是:共有,受保护,默认,私有
- 调用构造方法:Constructor类的newInstance(Object … params)
注意:如果没有访问权限,需要设置“暴力访问”:Constructor了类的setAccessible(true);
代码演示:
测试类:
public class Demo04 {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("com.itheima.demo04_获取构造方法并创建对象_Constructor.Student");
//2.获取构造方法
System.out.println("【获取所有[公有]构造方法】");
Constructor[] cs1 = stuClass.getConstructors();
for (Constructor c : cs1) {
System.out.println(c);
}
System.out.println("【获取所有构造方法】");
Constructor[] cs2 = stuClass.getDeclaredConstructors();
for (Constructor c : cs2) {
System.out.println(c);
}
System.out.println("【获取公有、无参的构造方法】");
Constructor c1 = stuClass.getConstructor();
System.out.println(c1);
Object o1 = c1.newInstance();//相当于:Student stu = new Student();
System.out.println("【获取公有、String、double参数的构造方法】");
Constructor c2 = stuClass.getConstructor(String.class, double.class);
System.out.println(c2);
Object o2 = c2.newInstance("呵呵", 3.14);
System.out.println("【获取私有的、String、int参数的构造方法】");
Constructor c3 = stuClass.getDeclaredConstructor(String.class, int.class);
System.out.println(c3);
c3.setAccessible(true);//暴力访问——绕过权限检查
Object o3 = c3.newInstance("哈哈", 120);
}
}
Student类
public class Student {
public Student(String s, double d) {
System.out.println("调用了公有、String、double参数的构造方法:s = " + s + " d = " + d);
}
public Student(){
System.out.println("调用了公有无参的构造方法...");
}
protected Student(int a) {
System.out.println("调用了受保护的构造方法...");
}
Student(String s) {
System.out.println("调用了默认的构造方法...");
}
private Student(String s, int a) {
System.out.println("调用了私有的构造方法:s = " + s + " a = " + a);
}
}
代码图解
获取成员属性并赋值和取值-Field
作用:获取一个类的“属性”,并且在创建此类对象后,为这个属性赋值,取值。
- 批量获取:
- public Field[] getFields();//获取所有的“公有”成员属性。
- public Field[] getDeclaredFields();获取所有的成员属性,包括:公有,受保护,默认,私有
- 获取单个:
- public Field getField(String fieldName);//获取“某个”,“公有”的成员属性
- public Field getDeclaredField(String fieldName);//获取某个成员属性,可以是:公有,受保护,默认,私有
- 为某个对象的属性设置值:
Field类的set(Object targetObj,Object value) - 获取某个对象的属性值:Field类的get(Object targetObj)
- 如果没有访问权限,需要设置“暴力访问”:Field类的setAccessible(true)
代码演示:
测试类
public class Demo05 {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("com.itheima.demo05_获取成员属性并赋值和取值_Field.Student");
//2.获取成员属性
System.out.println("【获取公有的name属性】");
Field nameField = stuClass.getField("name");
System.out.println(nameField);
System.out.println("【获取私有的sex属性】");
Field sexField = stuClass.getDeclaredField("sex");
System.out.println(sexField);
//要使用这个属性,必须要先创建对象
Object o = stuClass.getConstructor().newInstance();
System.out.println(o);
//为o对象的name属性赋值为:成龙
nameField.set(o, "成龙");
System.out.println(o);
//为o对象的sex属性赋值为:男
sexField.setAccessible(true);
sexField.set(o, '男');
System.out.println(o);
System.out.println("获取某个属性的值:" + nameField.get(o) + " , " + sexField.get(o));
}
}
Student类
package com.itheima.demo05_获取成员属性并赋值和取值_Field;
public class Student {
//编译器会自动添加一个:公有、无参、什么都不做的构造方法
public String name;
protected int age;
double height;
private char sex;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
", sex=" + sex +
'}';
}
}
代码图解
获取成员方法并调用method
- 批量获取:
- public Method[] getMethods();//虎丘所有的“公有”成员方法。
- public Method[] getDeclaredMethods();//获取所有的成员方法,包括:公有,受保护,默认,私有
- 获取单个:
- public Method getMethod(String methodName,Class…params);//获取某个公有的成员方法
- public Method getDeclaredMethod(String methodName,Class … params);//获取某个成员方法,可以是:公有,受保护,默认,私有。
- 调用方法:Method类的invoke(ObjecttargetObj,Class … params)
- 如果没有访问权限,需要设置“暴力访问”:Method的setAccessible(true)
代码演示:
package com.itheima.demo06_获取成员方法并调用_method;
import java.lang.reflect.Method;
public class Demo06 {
public static void main(String[] args) throws Exception {
//1.获取Class
Class stuClass = Class.forName("com.itheima.demo06_获取成员方法并调用_method.Student");
System.out.println("【获取公有、无参、无返回值的show1】");
Method m1 = stuClass.getMethod("show1");
System.out.println(m1);
System.out.println("【获取私有、有参、有返回值的show2】");
Method m2 = stuClass.getDeclaredMethod("show2", String.class, double.class);
System.out.println(m2);
//要调用方法,必须创建对象
Object o = stuClass.getConstructor().newInstance();
//调用m1方法
m1.invoke(o);
//调用m2方法
m2.setAccessible(true);
Object result = m2.invoke(o, "成龙", 1.80);
System.out.println("返回值:" + result);
}
}
package day26练习代码;
public class Student {
private String name;
private int age;
public String sex;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
public static void method1(){
System.out.println("公共,无参,无返回值方法");
}
public static String method2(){
System.out.println("公共,无参,有返回值方法");
return "返回值";
}
public static String method3(String name,int age){
System.out.println("公共,有参,有返回值方法:"+name+age);
return "有返回值";
}
private int method04(String name,int age){
System.out.println("公共,有参,有返回值方法:"+name+age);
return 0;
}
//构造方法:
public Student() {
System.out.println("公有,无参构造方法");
}
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("公有,全参构造方法");
}
private Student(String name){
System.out.println("私有,name属性构造方法");
}
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;
}
}
代码图解:
反射练习(面试题)
现有一个集合ArrayList,要求程序要向这个集合对象中添加一个String对象
//泛型——只存在于编译期,编译后没有泛型——泛型擦除
//比如我们自定义MyArrayList类:
class MyArrayList<E>{//编译后,生成class文件,没有<E>泛型了
public void add(E e) {//编译后,生成class文件,变为:add(Object o)
}
}
public class Demo07 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.现有一个集合
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
//2.要求程序向这个集合中添加一个String对象
// list.add("30");//编译错误
//通过"反射"绕过泛型检查——一个类的泛型,只存在于"编译期",编译后就没有泛型信息了,全部变为Object——泛型擦除
//1.获取AraryList的Class对象
Class listClass = list.getClass();
//2.通过反射的方式获取add()方法的Method对象
Method addMethod = listClass.getMethod("add", Object.class);
//3.调用add
addMethod.invoke(list, "呵呵");
//遍历
for (Object o : list) {
System.out.println(o + " 类型:" + o.getClass().getName());
}
}
}