JAVA_反射机制(照镜子)

反射

首先反射,当你照镜子的时候你的面部的个个部位都会通过镜子反射出来,让你看道你自己。而JAVA也会像我们一样,也有一面镜子,它可以通过这面镜子,看到自己的一些信息。JAVA反射机制就是对于一段程序,在运行的时候才知道要准备操作的类是什么,并可以在运行的时候获取类的完整构造,并调用对应的方法,获取.class文件。而我们就可以通过java反射机制得到一些程序中的类的一些具体信息,参数,方法等。JAVA反射机制用在并不知道初始化的类对象是什么,然而我们无法使用new关键字来创建一个对象对它的方法属性进行调用。这时我们就利用JAVA反射机制,动态获取对象的信息,和调用方法,这一功能就是反射机制。

Class

对于java反射机制的原理就是在Class类中,首先简单讲解一下class类这里并不会主要解释,主要是涉及一些方法去使用。首先官方解释为,**Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。**也就是说Class类保存这些类型信息的类。当加载一个.class文件时,就创建这个类的class对象,之后的对象都是由这个class对象创建的,所以对于同一个类的所有对象,class对象只有一个。

A a = new A(); A b = new A(); a.class = b.class;

Class无公共构造方法,Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。而对于反射机制来说,就是将java类中的各种成分映射成一个个java类的对象,对于一个类的方法,构造方法,变量等,这些成员在加入类中都会有一个Class类来描述。
接下来主要简单讲解一下我们通过正常new()的方式去创建一个对象是如何实现的。
首先我们一个类中的构造方法,变量,方法等相关信息都是在class类中,当我们对类Anew() 一个对象aJVM 会去加载A.class,将会在磁盘找到A.class文件,并加载到JVM内存中,这时我们创建的A类的对象空间就会去指向A类中的相关信息,而A类的信息都是由class对象指向的,所以也可这样说A类的对象可以有多个,但是class的对象只有一个。
接下来就将简单介绍几个Class类的方法。

1.static Class<?> forName(String className) //返回与带有给定字符串名的类或接相关联的 Class 对象 
2.String getName() //以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
3. T newInstance() //创建此 Class 对象所表示的类的一个新实例。  

并不是Class类的方法只有这几个,而是对于反射机制,我们会用到这几个方法,但是进入反射实战之前,我们先简单说一下 newInstance() ;
该方法可以创建class对象所表示的类的新实例,而他和我们去new()一个方法有什么区别呢??
首先 newInstance() :弱类型,低效率,只能调用无参的构造方法。
new():强类型,相对高效,能调用任何public构造。
下面我还是通过一段代码看看 newInstance()具体如何实现的。

package java_反射机制之class类创建一个实例;
import java.util.concurrent.Callable;
import java.lang.Class;
import static java.lang.Class.forName;
public class test {
    public static void main(String[] args) throws Exception{
        //test2 t2 = new test2();
        Class c = Class.forName("java_反射机制之class类创建一个实例.test2");//必须传入一个包下的一个路径以包.类。
        test2 t = (test2) c.newInstance();//并将类实例化,只能调用无参构造方法。
        t.show();
    }
}
interface test1{
    abstract void show();
}
class test2 implements test1{
    @Override
    public void show() {
        System.out.println("使用接口");
    }
}
运行结果:使用接口

这里去使用 newInstance()去创建test2()类的一个实例,然后通过该实例去调用接口中的方法。然后对于newInstance()所返回的类型是Object类型,所以我们要其进行强制转型。

反射实战

首先对于反射我们先要

  1. 获取反射的class对象
  2. 2.通过反射创建类对象
  3. 3.通过反射获取类属性及构造器
    首先对于第一部最重要就是我们如何获取反射的class类的对象。这里我们比较常用的就是 **Class.forName(String className) **
    接下来我们先用反射去获取不同修饰类型的构造方法,在这之前先了解一下相关方法:
/*1.获取构造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"构造方法
            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

 * 		2).获取单个的方法,并调用:
 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 *
 * 			调用构造方法:
 * 			Constructor-->newInstance(Object... initargs)
 * /
package java_反射机制获取构造方法;
import java.lang.Class;
import java.lang.reflect.Constructor;

public class test {
    test(String str){
        System.out.println("默认的构造方法"+str);
    }
    public test(){
        System.out.println("调用共有无参构造方法并执行");
    }
    public test(int age){
        System.out.println("调用共有有参构造方法 "+ age);
    }
    private test(char name){
        System.out.println("调用私有有参构造方法"+name);
    }
    protected test(boolean n){
        System.out.println("调用受保护的构造方法 "+n);
    }
    public static void main(String[] args) throws Exception {
     Class c = Class.forName("java_反射机制获取构造方法.test");
        System.out.println("****所有共有构造方法******");
        Constructor[] con = c.getConstructors();
        for(Constructor i:con){
            System.out.println(i);
        }
        System.out.println("*****获取单个共有构造方法并调用******");
        Constructor cn = c.getConstructor(int.class);
        System.out.println(cn);
        //cn.setAccessible(true);//若是在另外一个类中去调用时必须使用,这里再同一个类下不需要暴力反射
        cn.newInstance(10);
        System.out.println("*****获取某个受保护的构造方法并调用");
        Constructor cn1 = c.getDeclaredConstructor(boolean.class);
        System.out.println(cn1);
        cn1.newInstance(true);
    }
}
class test2{
    public static void main(String[] args) throws Exception{
        Class c = Class.forName("java_反射机制获取构造方法.test");
        System.out.println("***单个获取私有构造方法并调用*****");
        Constructor cn = c.getDeclaredConstructor(char.class);
        cn.setAccessible(true);//这里使用到了暴力访问,强制忽略私有修饰符。
        cn.newInstance('a');
    }
}
运行结果:
****所有共有构造方法******
public java_反射机制获取构造方法.test(int)
public java_反射机制获取构造方法.test()
*****获取单个共有构造方法并调用******
public java_反射机制获取构造方法.test(int)
调用共有有参构造方法 10
*****获取某个受保护的构造方法并调用
protected java_反射机制获取构造方法.test(boolean)
调用受保护的构造方法 true
test2运行结果:
***单个获取私有构造方法并调用*****
调用私有有参构造方法a

这里主要简单讲解一下关于如何获取构造方法,并去调用。这里能我创建了两个类,在不同的类下去反射获取构造方法并去调用,我们先来简单讲解一下在test类下的调用方法:
首先就是先获取class类的对象,然后通过调用获取单个构造方法的方法去获取构造方法,之后去调用构造方法,这是简单的流程。
对于test2 类中对于去调用私有构造方法时,使用到了**setAccessible()**暴力访问,强制忽略私有修饰符。
接下来我们对于反射获取方法并调用的代码演示
先让我们看一下使用到相关方法:

 /*
 * 获取成员方法并调用:
 *
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 *
 * 	 调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
):
 */
package java_反射机制获取成员方法;
import java.lang.reflect.Method;
public class test {
    public void show(String s){
        System.out.println("获取有参公开方法"+s);
    }
    public void show1(){
        System.out.println("获取无参公开方法");
    }
    private void show2(int i){
        System.out.println("获取私有构造方法"+i);
    }
    public static void main(String[] args)throws Exception{
     Class c = Class.forName("java_反射机制获取成员方法.test");
        System.out.println("或许所有公开方法");//可能获取包括obiect方法
        Method[] m = c.getMethods();
        for(Method i:m){
            System.out.println(i);
        }
        System.out.println("获取公开方法并调用");
        Method m1 = c.getMethod("show", String.class);
        test t = (test)c.newInstance();
        m1.invoke(t,"***");
    }
}
class test1 {
    public static void main(String[] args)throws Exception{
        Class c = Class.forName("java_反射机制获取成员方法.test");
        System.out.println("异类获取并调用私有方法");
        Method m = c.getDeclaredMethod("show", String.class);
        test t = (test)c.newInstance();
        m.setAccessible(true);//强制获取私有方法
        m.invoke(t,"这里是异类");
    }
}
运行结果:
public static void java_反射机制获取成员方法.test.main(java.lang.String[]) throws java.lang.Exception
public void java_反射机制获取成员方法.test.show(java.lang.String)
public void java_反射机制获取成员方法.test.show1()
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()
获取公开方法并调用
获取有参公开方法***

从运行结果也不难看出,我们获取的方法包含了父类的方法,也包括了Object类的方法。就主要简单讲解一下如何调用方法的,首先也是获取单个你需要的方法,之后这里就使用到了刚刚通过 newInstance() 去创建一个新的实列,然后通过该实例去对获取的方法调用,并赋值。而对于异类中对于获取到的私有方法的调用也是用到了**setAccessible()**去暴力访问。
接下来就是关于成员变量的获取和调用
先看一下相关方法:

/*
         * 获取成员变量并调用:
         *
         * 1.批量的
         * 		1).Field[] getFields():获取所有的"公有字段"
         * 		2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
         * 2.获取单个的:
         * 		1).public Field getField(String fieldName):获取某个"公有的"字段;
         * 		2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
         *
         * 	 设置字段的值:
         * 		Field --> public void set(Object obj,Object value):
         * 					参数说明:
         * 					1.obj:要设置的字段所在的对象;
         * 					2.value:要为字段设置的值;
         *
         */
package java_反射机制获取成员变量;
import java.lang.reflect.Field;
public class test {
    public String name;//设置公共属性姓名
    protected int age;
    private String phone;
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    @Override
    public String toString() {
        return "test{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
    public static void main(String[] args)throws Exception{
	Class c = Class.forName("java_反射机制获取成员变量.test");
        System.out.println("*****获取共有字段*****");
        Field[] fi = c.getFields();
        for(Field i:fi){
            System.out.println(i);
        }
        System.out.println("*****单个获取共有字段并赋值*******");
        Field fi1 = c.getField("name");
        System.out.println(fi1);
        test t = (test)c.newInstance();//创建一个对象
        fi1.set(t,"***");//通过引用该对象去赋值
        System.out.println(t.name);
    }
}
class test2{//不同类中使用反射机制
    public static void main(String[] args)throws Exception{
        Class c = Class.forName("java_反射机制获取成员变量.test");
        System.out.println("*****不同类下获取私有字段并赋值******");
        Field fi = c.getDeclaredField("phone");
        System.out.println(fi);
        test t2 = (test)c.newInstance();
        fi.setAccessible(true);//暴力反射,解除私有限定
        fi.set(t2,"**");
        System.out.println(t2.getPhone());
        }
}
运行结果:
*****获取共有字段*****
public java.lang.String java_反射机制获取成员变量.test.name
*****单个获取共有字段并赋值*******
public java.lang.String java_反射机制获取成员变量.test.name
***

这里同理也是使用到了 newInstance()去创建一个新的实例,然后对于获取到的变量进行赋值操作,对于异类中,对于私有变量的调用,也是使用到了setAccessible()进行暴力访问。
接下来我个人觉得较为有用的就是关于反射机制越过泛型直接看例子

package java_反射机制越过泛型;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

public class test {
    public static void main(String[] args)throws Exception{
        ArrayList<String>  l = new ArrayList<>();
        l.add("a");
        l.add("b");
        Class c= l.getClass();//因为已经创建对象了。
        Method m = c.getMethod("add",Object.class);
        m.invoke(l,10);
        for(Object i:l){
            System.out.println(i);
        }
    }
}
运行结果:
a
b
10

程序只要就是起先定义一个String类型的集合,但此时我想要往集合内存Integer类型的变量,这里我就将通过反射机制获取ArrayList下的add()方法,然后进行赋值,之后再对集合进行遍历。这里我们使用到getClass()方法去获取Class类的对象,因为之前已经创建好了ArrayList的一个对象。
好了到这为止对于java反射机制的相关使用简单的整理了一下,唯一还有就是通过反射机制获取main下的方法这里就通过一段代码展示,与上面的原理相同

package java_反射机制反射mian的方法;

import java.lang.reflect.Method;

public class test {
    public static void main(String[] args){
        System.out.println("这里是main函数的方法");
        for(int i=0;i<args.length;i++){
            System.out.println(args[i]);
        }
    }
}
class test1{
    public static void main(String[] args)throws Exception{
        Class c = Class.forName("java_反射机制反射mian的方法.test");
        Method m = c.getMethod("main", String[].class);
        m.invoke(null,(Object)new String[]{"a,b,c"});
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章