目录
相关视频:
相关文章:
什么是反射、反射的三种方式等:Java重点基础:反射机制
点击量34万的一篇文章:Java基础之—反射(非常重要)
反射面试题:反射面试题-请了解下
1、基本概念与三种获取Class对象的方式
1.1、基本概念:
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
1.2、在面向对象的世界里,万事万物皆对象。
Java语言中,有两个东西不是面向对象的:普通的数据类型和静态的成员。
普通的数据类型:例如:int a = 5; 不是面向对象的,但它有封装类弥补了这点;
静态的成员也是不属于某个对象的,它是属于类的。
总体来讲,万事万物皆对象是没有错的。
1.3、类是对象吗?
是的,类是java.lang.Class类的实例对象。
/*
* Private constructor. Only the Java Virtual Machine creates Class objects.
* This constructor is not used and prevents the default constructor being
* generated.
*/
private Class(ClassLoader loader) {
classLoader = loader;
}
可以看到Class的构造函数是私有的,因此是不能被new的,而注释里也说明了,只有java虚拟机才能create Class对象。
1.4、三种获取Class对象的方式:
public class ClassDemo1 {
public static void main(String[] args) {
//Foo的实例对象如何表示,如下的 foo1 就是
Foo foo1 = new Foo();
//Foo这个类 也是一个实例对象,Class类的实例对象,如何表示呢?
//任何一个类都是Class的实例对象,这个实例对象有三种表示方式
//第一种表示方式 --> 实际在告诉我们任何一个类都有一个隐含的静态成员变量class
Class c1 = Foo.class;
//第二种表示方式 --> 已经知道该类的对象,通过getClass方法
Class c2 = foo1.getClass();
/*** 在官网上,为了避免混淆,把c1、c2成为Foo类的类类型(即class type)
* 万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称之为该类的类类型*/
//不管c1 or c2都代表了Foo类的类类型,一个类只可能是Class类的一个实例对象
System.out.println(c1 == c2);
//第三种表示方式
Class c3 = null;
try {
c3 = Class.forName("com.ph.reflect.Foo");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c2 == c3);
//我们完全可以通过类的类类型创建该类的对象实例 --> 通过c1 or c2 or c3创建Foo的实例
try {
//这样使用的前提:需要有无参数的构造方法
Foo foo = (Foo) c1.newInstance();
foo.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Foo {
void print(){
System.out.println("Foo");
}
}
Class.forName("类的全称")
- 不仅表示了类的类类型,还代表了动态加载类
- 请大家区分编译、运行
- 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
new 创建对象是静态加载类,在编译时刻就需要加载所有的可能使用到的类,不管你有没有用到
那么,我们能不能在使用到这个类的时候再加载这个类呢?
通过动态加载类就可以解决这个问题
class office{
public static void main(String[] args) {
if ("Word".equals(args[0])) {
Word w=new Word();
w.start();
}
if ("Excel".equals(args[0])) {
Excel e=new Excel();
e.start();
}
}
}
class OfficeBetter{
public static void main(String[] args) {
try {
Class c = Class.forName(args[0]);
OfficeAble oa = (OfficeAble)c.newInstance();
oa.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
interface OfficeAble{
public void start();
}
class Word implements OfficeAble{
public void start() {
System.out.println("word ... start...");
}
}
以上都是通过 javac、java等命令进行操作。
public class ClassDemo2 {
public static void main(String[] args) {
Class c1 = int.class;//int 的类类型
Class c2 = String.class;//String类 的类类型 String类字节码
Class c3 = double.class;
Class c4 = Double.class;
Class c5 = void.class;
System.out.println(c1.getName());
System.out.println(c2.getName());
System.out.println(c3.getName());
System.out.println(c4.getName());
System.out.println(c5.getName());
}
}
2、获取方法信息
public class ClassDemo3 {
public static void main(String[] args) {
Person person = new Person();
ClassUtil.printClassMessage(person);
}
}
public class ClassUtil {
/**
* 打印类的信息,包括类的成员函数、成员变量
*
* @param object 该对象所属类的信息
*/
public static void printClassMessage(Object object) {
//要获取类的信息 首先要获取类的类类型
Class c = object.getClass();//传递的是哪个子类的对象 c就是该子类的类类型
//获取类的名称
System.out.println("类的名称是:" + c.getName());
/**
* Method类,方法对象
* 一个成员方法就是一个Method对象
* getMethods()方法获取的是所有的public的函数,包括父类继承而来的
* getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
*/
Method[] ms = c.getMethods();//c.getDeclaredMethods();
for (int i = 0; i < ms.length; i++) {
//得到方法的返回值类型的类类型
Class returnType = ms[i].getReturnType();
System.out.print(returnType.getName() + " ");
//得到方法的名称
System.out.print(ms[i].getName() + "(");
//获取参数类型 --> 得到的是参数列表的类型的类类型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
}
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
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 int compareTo(Person person) {
int temp = this.age - person.age;
return temp == 0 ? this.name.compareTo(person.name) : temp;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3、获取成员变量、构造函数信息
public class ClassDemo3 {
public static void main(String[] args) {
Cat cat = new Cat();
ClassUtil.printFieldMessage(cat);
System.out.println("================");
ClassUtil.printMethodMessage(cat);
ClassUtil.printConstructorMessage(cat);
ClassUtil.printConstructorMessage(new Integer(1));
}
}
public class Cat {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ClassUtil {
/**
* 获取对象的成员函数的信息
*
* @param object 该对象所属类的信息
*/
public static void printMethodMessage(Object object) {
//要获取类的信息 首先要获取类的类类型
Class clazz = object.getClass();//传递的是哪个子类的对象 c就是该子类的类类型
//获取类的名称
System.out.println("类的名称是:" + clazz.getName());
/**
* Method类,方法对象
* 一个成员方法就是一个Method对象
* getMethods()方法获取的是所有的public的函数,包括父类继承而来的
* getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
*/
Method[] ms = clazz.getMethods();//c.getDeclaredMethods();
for (int i = 0; i < ms.length; i++) {
//得到方法的返回值类型的类类型
Class returnType = ms[i].getReturnType();
System.out.print("返回值类型:" + returnType.getName() + " ");
//得到方法的名称
System.out.print("方法名称:" + ms[i].getName() + "(");
//获取参数类型 --> 得到的是参数列表的类型的类类型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
/**
* 获取对象的成员变量的信息
*
* @param object 该对象所属类的信息
*/
public static void printFieldMessage(Object object) {
Class clazz = object.getClass();
/**
* 成员变量也是对象
* java.lang.reflect.Field
* Field类封装了关于成员变量的操作
* getFields()方法获取的是所有的public的成员变量的信息
* getDeclaredFields()获取的是该类自己声明的成员变量的信息
*/
// Field[] fs = clazz.getFields();
Field[] fs = clazz.getDeclaredFields();
for (Field field : fs) {
//得到成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成员变量的名称
String fieldName = field.getName();
System.out.println("成员变量类型:" + typeName + " " + "成员变量名称:" + fieldName);
}
}
/**
* 获取对象的构造函数的信息
*
* @param object 该对象所属类的信息
*/
public static void printConstructorMessage(Object object) {
Class clazz = object.getClass();
/**
* 构造函数也是对象
* java.lang.Constructor中封装了构造函数的信息
* getConstructors() 获取所有的public的构造函数
* getDeclaredConstructors() 获取所有的构造函数
*/
// Constructor[] cs2 = clazz.getConstructors();
Constructor[] cs = clazz.getDeclaredConstructors();
for (Constructor constructor : cs) {
System.out.print("构造函数:" + constructor.getName() + "(");
//获取构造函数的参数列表 --> 得到的是参数列表的类类型
Class[] paramsTypes = constructor.getParameterTypes();
for (Class class1:paramsTypes){
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
}
4、方法反射的基本操作
public class MethodDemo1 {
public static void main(String[] args) {
//要获取print(int ,int)方法
//1.要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型
A a = new A();
Class clazz = a.getClass();
/**
* 2.获取方法 名称和参数列表来决定
* getMethod() 获取的是public方法
* getDeclaredMethod() 自己声明的方法
*/
try {
Method m = clazz.getMethod("print", int.class, int.class);
//方法的反射操作:用m对象来进行方法调用,和a.print(10,10)的调用效果完全相同
// a.print(10,10);
//方法如果没有返回值返回null,有返回值的返回具体的返回值
m.invoke(a, 10, 20);
System.out.println("==================");
Method m2 = clazz.getMethod("print", String.class, String.class);
// a.print("aa","bb");
m2.invoke(a, "aa", "bb");
System.out.println("==================");
Method m3 = clazz.getMethod("print");
// a.print();
m3.invoke(a);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class A {
public void print() {
System.out.println("hello world");
}
public void print(int a, int b) {
System.out.println(a + b);
}
public void print(String a, String b) {
System.out.println(a.toUpperCase() + ", " + b.toUpperCase());
}
}
5、通过反射了解集合泛型的本质
public class ClassDemo4 {
public static void main(String[] args) {
ArrayList arrayList1 = new ArrayList();
ArrayList<String> arrayList2 = new ArrayList<>();
arrayList2.add("hello");
// arrayList2.add(20); 编译错误
Class clazz1 = arrayList1.getClass();
Class clazz2 = arrayList2.getClass();
System.out.println(clazz1 == clazz2);
System.out.println("=====================");
/**
* clazz1 == clazz2 结果返回true说明编译之后集合都是被去泛型化的
* Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译阶段(即在运行阶段)就没有效果了
* 验证:我们可以通过方法的反射来操作,绕过编译
* 反射的操作都是在编译阶段之后的操作
*/
try {
Method method = clazz2.getMethod("add", Object.class);
method.invoke(arrayList2,20);//绕过编译阶段就绕过了泛型
System.out.println(arrayList2);
//不能这样遍历了,会抛出java.lang.ClassCastException
// for (String string:arrayList2) {
// System.out.println(string);
// }
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
6、手写butterknife框架
注解类:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoBindView {
@IdRes
int value();
}
注解辅助类:
public class AutoBindViewUtils {
public static void autoBindView(Activity activity) {
Class<? extends Activity> cls = activity.getClass();
// 获取成员变量
// cls.getFields(); 获得自己+父类的成员(不包括private,只能是public)
// cls.getDeclaredFields(); 获得自己的成员(获取所有作用域的,包括public和private,但是不包括父类的成员)
// cls.getSuperclass().getDeclaredFields(); 获取父类所有作用域的成员
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
//判断属性是否被 autoBindView 注解
if (field.isAnnotationPresent(AutoBindView.class)) {
AutoBindView autoBindView = field.getAnnotation(AutoBindView.class);
//获得注解中设置的值
int id = autoBindView.value();
View view = activity.findViewById(id);
//反射设置属性的值
field.setAccessible(true);//设置访问权限,允许访问private的属性
try {
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
界面:
public class MainActivity extends AppCompatActivity {
@AutoBindView(R.id.tv)
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AutoBindViewUtils.autoBindView(this);
tv.setText("哈哈");
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>