首页 专题 文章 代码 归档
Java 反射基础
2020.03.02 10:37 2020.03.02 10:38

1. 关于反射的问题

新手在学习Java的反射时,一般都有疑问;那我们先从解答疑问开始;

1、什么是反射机制?

简单的来说,反射机制指的是程序在运行时能够获取自身的信息。

在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。

2、哪里用到反射机制 ?

其实很多地方都用到了,特别是各种框架,如Spring全家桶、Mybatis框架等等;

可以说,正因为有了反射和注解,Spring等框架才能如此方便,以后有机会学到Spring了就能理解;

3、反射机制的优点与缺点

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念

  • 静态编译:在编译时确定类型,绑定对象,即通过。
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。

比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。

采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。但这类操作总是慢于只直接执行相同的操作。

2. Class类的使用

在面向对象的世界里,万事万物皆为对象:Java中类是对象,是java.lang.Class的对象。

本质上讲,一个对象对应的一个不变的、唯一的Class对象,称之为该对象的类类型(class type),它代表着一个类的类型信息。

Class类能够实现它所代表的这个类的所有功能,包括创建这个类的实例,获得所有的构造函数,方法,字段值等。


实例:

User.java

package test13;

public class User {

    private String name;
    private int age;

    public User() {
    }

    public User(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;
    }
}

测试Main.java

public static void main(String[] args) {
    User u = new User();
    // 其实User也是个实例对象,Class<User>的实例对象,如何表示呢
    // 任何一个类都有一个对应的Class实例对象,该Class实例对象有三种表示方法

    /*方法一*/
    // 这告诉我们任何一个类都有一个隐含的静态成员变量class
    Class<User> class1 = User.class;
    /*方法二*/
    // 通过该类实例对象的 getClass() 方法获取
    Class<? extends User> class2 = u.getClass();
    /*方法三*/
    Class<?> class3 = null;
    try {
        class3 = Class.forName("test13.User");//这里需要填全限定类名(即还要有包名)
        // 也许JVM找不到我们写的类名,所以抛出异常,因此需要捕获;
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    // 它们相等吗
    System.out.println(class1 == class2);//true
    System.out.println(class1 == class3);//true
    /*证明了一个类的Class是唯一的不变的*/


}

上面的代码就是我们获取一个类的类对象的方法,获取到这些class有什么用?

可以调用这些方法创建出实例对象

private static void test02() {
    /*方法三*/
    try {
        Class<?> class3 = Class.forName("test13.User");

        User  o = (User) class3.getDeclaredConstructor().newInstance();
        /*通过class3我们就得到一个实例对象*/

        //  class3.getDeclaredConstructor().newInstance();是新的方法,以前还有一个废弃的方法
        // 叫class3.newInstance()

    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
        e.printStackTrace();
    }
}

是不是很麻烦?那为什么不直接new出实例对象,而要通过这么麻烦的方法?

1、有一定开发经验的童鞋都知道,很多东西写死在程序里面是完全不好的,这会导致:想要修改一些东西,必须修改源代码后、编译、发布、部署等等步骤,异常麻烦;这是其一;

2、使用new创建对象属于静态加载类,无论该类是否能用到,在编译时期就要加载所有可能用到的类,如果编译时期加载的类很多不但影响系统启动速度与性能而且一旦一个类报错改程序将无法运行。


如何证明以上第二点?

使用记事本编写以下代码,然后使用javac命令行编译:

class Office {
    public static void main(String[] args) {
        if("Word".equals(args[0])) {
            Word word=new Word();
            word.start();
        }
        if("Excel".equals(args[0])) {
            Excel excel=new Excel();
            excel.start();
        }
    }
}

会报错:

截图-1583115025

加上Word:

class Office {
    public static void main(String[] args) {
        if("Word".equals(args[0])) {
            Word word=new Word();
            word.start();
        }
        if("Excel".equals(args[0])) {
            Excel excel=new Excel();
            excel.start();
        }
    }
}

class Word{
    public void start() {
        System.out.println("Word Start");
    }
}

只报Excel的错了:

截图-1583115080

由此可见使用new创建对象属于静态加载类,无论该类是否能用到,在编译时期就要加载所有可能用到的类,如果编译时期加载的类很多不但影响系统启动速度与性能而且一旦一个类报错改程序将无法运行。


那么能否在运行时再加载需要的类呢?当然可以,这就是动态加载类。

修改代码:


import java.lang.reflect.InvocationTargetException;

class Office {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> c = Class.forName(args[0]);
        //通过类类型创建该类的对象,要想让Word和Excel都可以用,要进行统一
        //Word w=(Word)c.newInstance();
        OfficeAble oa = (OfficeAble) c.getDeclaredConstructor().newInstance();
        oa.start();
    }
}


//添加统一接口
interface OfficeAble {
    void start();
}

//Word实现OfficeAble
class Word implements OfficeAble {
    @Override
    public void start() {
        System.out.println("word...start...");
    }
}
/*//Excel实现OfficeAble
class Excel implements  OfficeAble{
    @Override
    public void start() {
        System.out.println("excel...start...");
    }
}*/

传递Word是正常的:

截图-1583115509

此时传递Excel,报错:

截图-1583115564

3. 反射

好了,经过前面的基础,正式进入到反射了:

3.1. 概念

Reflection(反射)是指在程序运行期间,能够获取任意一个类的所有属性和方法,能够任意操作一个对象的属性和方法。这是Java被称为动态语言的基础。

3.2. 作用

  • 运行时创建任何一个类的对象(该类必须有无参构造器)
  • 运行时获取任何一个类的所有构造器、方法和属性
  • 运行时调用任何一个对象的构造器、方法和属性
  • 生成动态代理

4. 获取

反射就是获取类的所有信息,构造器、字段、方法等等,获取后,就可以调用方法等等,进行正常实例的操作;

4.1. 构造器

对象:

package test13;

public class People {
    //-------------------------------构造器--------------------------------------------
    //无参构造器
    public People() {
        System.out.println("无参构造器");
    }

    //构造器
    public People(String name) {
        System.out.println("一个参数构造器 姓名:" + name);
    }

    //构造器
    public People(String name, int age) {
        System.out.println("两个参数构造器 姓名:" + name + "年龄:" + age);
    }

    //受保护构造器
    protected People(boolean pro) {
        System.out.println("受保护构造器 protected:" + pro);
    }

    //私有构造器
    private People(int age) {
        System.out.println("私有构造器private:" + age);
    }
}

测试:

private static void test03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //1.加载Class对象
    Class<People> clazz = People.class;


    //2.获取所有公有构造方法
    System.out.println("**********************所有公有构造方法*********************************");
    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println("constructor = " + constructor);
    }


    System.out.println("*******************所有的构造方法*************************************");
    constructors = clazz.getDeclaredConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println(constructor);
    }

    System.out.println("*****************获取公有、无参的构造方法*******************************");
    //需要一个参数的类型,无参构造器所有类型为null 或者不写
    Constructor<People> constructor = clazz.getConstructor();
    System.out.println("constructor = " + constructor);

    //调用构造方法
    Object obj = constructor.newInstance();

    System.out.println("******************获取私有构造方法,并调用*******************************");
    constructor = clazz.getDeclaredConstructor(boolean.class);
    System.out.println(constructor);
    //调用构造方法
    constructor.setAccessible(true);//暴力访问(忽略掉访问修饰符)
    obj = constructor.newInstance(false);

}

运行结果:

**********************所有公有构造方法*********************************
constructor = public test13.People(java.lang.String,int)
constructor = public test13.People(java.lang.String)
constructor = public test13.People()
*******************所有的构造方法*************************************
private test13.People(int)
protected test13.People(boolean)
public test13.People(java.lang.String,int)
public test13.People(java.lang.String)
public test13.People()
*****************获取公有、无参的构造方法*******************************
constructor = public test13.People()
无参构造器
******************获取私有构造方法,并调用*******************************
protected test13.People(boolean)
受保护构造器 protected:false

Process finished with exit code 0

4.2. 属性

属性类,People.java

package Test14;

public class People {

    public People() {
    }

    public String name;
    protected int age;
    char sex;
    private int height;

    //添加height的get方法
    public int getHeight() {
        return height;
    }
}

测试:

package Test14;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.获取Class对象
        Class<People> peopleClass = People.class;
        //2.获取字段
        System.out.println("************获取所有公有的字段********************");
        Field[] fieldArray = peopleClass.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        System.out.println("****************获取所有的字段***********************");
        fieldArray = peopleClass.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        System.out.println("*************获取公有字段、并调用***********************************");
        Field f = peopleClass.getField("name");
        System.out.println(f);
        //获取一个对象
        Object obj = peopleClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
        //为字段设置值
        f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
        //验证
        People people= (People) obj;
        System.out.println("验证姓名:" + people.name);


        System.out.println("**************获取私有字段、并调用********************************");
        f = peopleClass.getDeclaredField("height");
        System.out.println(f);
        //私有属性必须解除私有限定
        f.setAccessible(true);
        f.set(obj, 176);
        //关闭解除
        f.setAccessible(false);
        System.out.println("身高:" + people.getHeight());
    }
}

结果:

************获取所有公有的字段********************
public java.lang.String Test14.People.name
****************获取所有的字段***********************
public java.lang.String Test14.People.name
protected int Test14.People.age
char Test14.People.sex
private int Test14.People.height
*************获取公有字段、并调用***********************************
public java.lang.String Test14.People.name
验证姓名:刘德华
**************获取私有字段、并调用********************************
private int Test14.People.height
身高:176

4.3. 方法

方法类:

package test15;

public class People {
    public void method1(String s) {
        System.out.println("方法名为【method1】,公有的,无返回值,参数类型为String:" + s);
    }

    protected void method2() {
        System.out.println("方法名为【method2】,受保护的,无返回值,无参");
    }

    void method3() {
        System.out.println("方法名为【method3】,默认的,无返回值,无参");
    }

    private String method4(int age) {
        System.out.println("方法名为【method4】,私有的,有返回值,参数类型为int:" + age);
        return "哈哈";
    }
}

测试:

package test15;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.获取Class对象
        Class<People> peopleClass = People.class;
        //2.获取所有公有方法
        System.out.println("***************获取所有的公有方法*******************");
        peopleClass.getMethods();
        Method[] methodArray = peopleClass.getMethods();
        for (Method m : methodArray) {
            System.out.println(m);
        }
        System.out.println("***************获取所有的方法***********************");
        methodArray = peopleClass.getDeclaredMethods();
        for (Method m : methodArray) {
            System.out.println(m);
        }
        System.out.println("***************获取公有的method1()方法*******************");
        //public Method getMethod(String name,Class<?>... parameterTypes):
        Method m = peopleClass.getMethod("method1", String.class);
        System.out.println(m);
        //实例化一个Student对象
        Object obj = peopleClass.getConstructor().newInstance();
        m.invoke(obj, "刘德华");

        System.out.println("***************获取私有的method4()方法******************");
        //public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
        m = peopleClass.getDeclaredMethod("method4", int.class);
        System.out.println(m);
        //解除私有限定
        m.setAccessible(true);
        //需要两个参数,一个是要调用的对象,一个是实参
        Object result = m.invoke(obj, 28);
        System.out.println("返回值:" + result);
    }
}

结果:

***************获取所有的公有方法*******************
public void test15.People.method1(java.lang.String)
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
***************获取所有的方法***********************
protected void test15.People.method2()
void test15.People.method3()
public void test15.People.method1(java.lang.String)
private java.lang.String test15.People.method4(int)
***************获取公有的method1()方法*******************
public void test15.People.method1(java.lang.String)
方法名为【method1】,公有的,无返回值,参数类型为String:刘德华
***************获取私有的method4()方法******************
private java.lang.String test15.People.method4(int)
方法名为【method4】,私有的,有返回值,参数类型为int:28
返回值:哈哈
本节阅读完毕! (分享
二维码图片 扫描关注我们哟