Java学习笔记54(反射详解)

反射概念:

java反射机制是在运行状态中,对于任意一个类,都能知道所有属性和方法

对于任意一个对象都能调用它的任意一个方法和属性,这种动态获取和调用的功能称为java的反射机制

实际作用:

已经完成一个java程序,但是想再添加新功能,又不能修改源码,这时候就用到反射机制了

获取class文件的三种方式:

简单地自定义一个Person类:

package demo;

public class Person {
    public String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(int age, String name) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println("人吃饭");
    }

    public void sleep(String s, int a, double d) {
        System.out.println("人在睡觉" + s + "....." + a + "....." + d);
    }

    private void playGame() {
        System.out.println("人在打游戏");
    }

    public String toString() {
        return "Person [name=" + name + ", 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;
    }

}

获取person类class文件(三种方法本质上获取的是同一个对象):

package demo;

public class ReflectDemo {
    public static void main(String[] args) {
        //1.对象获取
        Person p = new Person();
        Class c = p.getClass();
        System.out.println(c);
        //2.类名获取
        Class c1 = Person.class;
        System.out.println(c1);

        System.out.println(c==c1);//true
        System.out.println(c.equals(c1));//true
        //只存在一个class文件,两种方式都是获得同一个对象

        //3.Class类的静态方法,参数注意带着包名防止重名
        try {
            Class c2 =     Class.forName("demo.Person");
            System.out.println(c2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

反射获取空参构造方法:

package demo;

import java.lang.reflect.Constructor;

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("demo.Person");
        function1(c);
        function2(c);
    }

    private static void function1(Class c) {
        // 获得所有公共权限(public)的构造器
        Constructor[] cons = c.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
            // 输出:
            // public demo.Person(java.lang.String,int)
            // public demo.Person()
        }
    }

    public static void function2(Class c) {
        // 获取空参构造器并执行(toString方法)
        try {
            Constructor con = c.getConstructor();
            Object obj = con.newInstance();
            System.out.println(obj);
            // 输出:Person [name=null, age=0]
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

反射获取有参构造方法:

package demo;

import java.lang.reflect.Constructor;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        Constructor con = c.getConstructor(String.class, int.class);
        System.out.println(con);
        Object object = con.newInstance("张三", 20);
        System.out.println(object);
    }
}
/*输出:
public demo.Person(java.lang.String,int)
Person [name=张三, age=20]
*/

发现上边的方式代码量偏大,快捷一些的方式:

前提:被反射的类,必须具有空参构造方法,且构造方法权限必须是public

package demo;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        // Class类中定义方法, T newInstance() 直接创建被反射类的对象实例
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

反射获取私有构造方法(日常开发不建议使用,这种方法了解即可):

package demo;

import java.lang.reflect.Constructor;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        // 获取所有构造方法(包括私有的)
        // Constructor[] cons = c.getDeclaredConstructors();
        Constructor con = c.getDeclaredConstructor(int.class, String.class);
        con.setAccessible(true);// 取消权限,破坏了封装性
        Object object = con.newInstance(18, "张三");
        System.out.println(object);
        // Person [name=张三, age=18]
    }
}

反射获取类的成员变量并修改:

package demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");

        // 获得所有public成员
        Field[] fields = c.getFields();
        // 获得所有成员变量
        Field[] fields2 = c.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        // 获取指定成员变量并修改
        Field field = c.getField("name");
        Object object = c.newInstance();
        field.set(object, "张三");
        System.out.println(object);
        // 输出:Person [name=张三, age=0]
    }
}

反射获取成员方法并执行:

package demo;

import java.lang.reflect.Method;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("demo.Person");
        // 获得所有public方法
        /*
         * Method[] methods = c1.getMethods();
         * for(Method method:methods){
         * System.out.println(method); }
         */

        // 获取指定方法运行(空参)
        Method method = c1.getMethod("eat");
        Object obj = c1.newInstance();
        method.invoke(obj);
        // 输出:人吃饭

        // 有参
        Method method1 = c1.getMethod("sleep", String.class, int.class, double.class);
        Object obj1 = c1.newInstance();
        method1.invoke(obj1, "休息", 10, 10.11);
        // 输出:人在睡觉休息.....10.....10.11

        // 可以利用前面提到的方法暴力运行私有方法
    }
}

反射的泛型擦除:

package demo;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        ArrayList<String> array = new ArrayList<String>();
        // 通常添加方式
        array.add("a");
        array.add("1");

        Class class1 = array.getClass();
        Method method = class1.getMethod("add", Object.class);
        method.invoke(array, 100);
        method.invoke(array, 666.666);
        method.invoke(array, 0.1);
        System.out.println(array);
        // 输出:[a, 1, 100, 666.666, 0.1]
    }
}

反射实现通过配置文件运行:

有时候想改源码,但是不能改源码,可以这样做:

自定义三个类:

package demo;

public class Person {
    public void eat(){
        System.out.println("人在吃饭");
    }
}

package demo;

public class Student {
    public void study(){
        System.out.println("学生在学习");
    }
}

package demo;
public class Worker {
    public void job(){
        System.out.println("上班族在工作");
    }
}

配置文件:config.properties

#className=demo.Student
#methodName=study
className=demo.Person
methodName=eat
#className=demo.Worker
#methodName=job

测试类:

package demo;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

/*
 *  调用Person方法,调用Student方法,调用Worker方法
 *  类不清楚,方法也不清楚
 *  通过配置文件实现此功能
 *    运行的类名和方法名字,以键值对的形式,写在文本中
 *    运行哪个类,读取配置文件即可
 *  实现步骤:
 *    1. 准备配置文件,键值对
 *    2. IO流读取配置文件  Reader
 *    3. 文件中的键值对存储到集合中 Properties
 *        集合保存的键值对,就是类名和方法名
 *    4. 反射获取指定类的class文件对象
 *    5. class文件对象,获取指定的方法
 *    6. 运行方法
 */
public class Test {
    public static void main(String[] args) throws Exception{
        //IO流读取配置文件
        FileReader r = new FileReader("config.properties");
        //创建集合对象
        Properties pro = new Properties();
        //调用集合方法load,传递流对象
        pro.load(r);
        r.close();
        //通过键获取值
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        //反射获取指定类的class文件对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        //获取指定的方法名
        Method method = c.getMethod(methodName);
        method.invoke(obj);
    }
}

原文地址:https://www.cnblogs.com/xuyiqing/p/8366483.html

时间: 2024-11-05 15:58:55

Java学习笔记54(反射详解)的相关文章

Java学习笔记--集合类型详解

http://www.cnblogs.com/xwdreamer/archive/2012/05/30/2526822.html http://blog.csdn.net/liulin_good/article/details/6213815 http://www.cnblogs.com/meng72ndsc/archive/2010/12/23/1914477.html http://www.cnblogs.com/ningvsban/archive/2013/05/06/3062535.ht

IOS开发学习笔记(1)-----UILabel 详解

1. [代码][C/C++]代码     //创建uilabelUILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(20, 40, 280, 80)];//设置背景色label1.backgroundColor = [UIColor grayColor];//设置taglabel1.tag = 91;//设置标签文本label1.text = @"Hello world!";//设置标签文本字体和字体大小label1.

IOS开发学习笔记(2)-----UIButton 详解

1. [代码][C/C++]代码     //这里创建一个圆角矩形的按钮    UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];    //    能够定义的button类型有以下6种,//    typedef enum {//        UIButtonTypeCustom = 0,          自定义风格//        UIButtonTypeRoundedRect,        

Java学习之道:详解Java解析XML的四种方法

XML现在已经成为一种通用的数据交换格式,它的平台无关性,语言无关性,系统无关性,给数据集成与交互带来了极大的方便.对于XML本身的语法知识与技术细节,需要阅读相关的技术文献,这里面包括的内容有DOM(Document Object Model),DTD(Document Type Definition),SAX(Simple API for XML),XSD(Xml Schema Definition),XSLT(Extensible Stylesheet Language Transform

CoAP学习笔记——CoAP格式详解

0 前言 CoAP是受限制的应用协议(Constrained Application Protocol)的代名词.在当前由PC机组成的世界,信息交换是通过TCP和应用层协议HTTP实现的.但是对于小型设备而言,实现TCP和HTTP协议显然是一个过分的要求.为了让小设备可以接入互联网,CoAP协议被设计出来.CoAP是一种应用层协议,它运行于UDP协议之上而不是像HTTP那样运行于TCP之上.CoAP协议非常小巧,最小的数据包仅为4字节. 更多博文请参考--[物联网学习笔记--索引博文] 1 Co

Struts2学习笔记(二)——配置详解

1.Struts2配置文件加载顺序: default.properties(默认常量配置) struts-default.xml(默认配置文件,主要配置bean和拦截器) struts-plugin.xml(配置插件) struts.xml(配置action或者常量等) struts.properties(常量配置) web.xml(配置JavaEE,如:监听器和过滤器) 2.Struts2配置文件详解 1)default.properties default.properties是Struts

Redis学习笔记4-Redis配置详解

原文:  http://blog.csdn.net/mashangyou/article/details/24555191 在Redis中直接启动redis-server服务时, 采用的是默认的配置文件.采用redis-server   xxx.conf 这样的方式可以按照指定的配置文件来运行Redis服务.按照本Redis学习笔记中Redis的按照方式按照后,Redis的配置文件是/etc/redis/6379.conf.下面是Redis2.8.9的配置文件各项的中文解释. 1 #daemon

Struts2学习笔记二 配置详解

Struts2执行流程 1.简单执行流程,如下所示: 在浏览器输入请求地址,首先会被过滤器处理,然后查找主配置文件,然后根据地址栏中输入的/hello去每个package中查找为/hello的namespace,然后在包下寻找名为HelloAction的action,反射创建控制器HelloAction对象,调用method指定的方法hello,拿到返回值“success”,去result的配置节找success对应的页面(hello.js),呈现给用户. 2.Struts的架构,具体参考这里

Java学习笔记之反射

鲁春利的工作笔记,谁说程序员不能有文艺范? Java程序在运行期间可以动态加载.解析和使用一些在编译阶段并不确定的类型,这一机制被称为反射(Reflection)或自省(Introspection).Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.这种"看透class"的能力(the ability of the program toexamine itself)被称为