JAVA进阶之旅(二)——认识Class类,反射的概念,Constructor,Fiald,Method,反射Main方法,数组的反射和实践

JAVA进阶之旅(二)——认识Class类,反射的概念,Constructor,Fiald,Method,反射Main方法,数组的反射和实践


我们继续聊JAVA,这次比较有意思,那就是反射了

一.认识Class类

想要反射,你就必须要了解一个类——Class,我们知道,java程序中的各个java类都属于同一事物,我们通常用Classliability描述对吧,反射这个概念从JDK1.2就出来了,历史算是比较悠久了,这个Class可不是关键字哦,这个是一个类,他代表的是一类事物;

  • 我们归根结底就是拿到字节码对象

这里我们有三种方式是可以得到对应的实例对象(Class类型)

  • 1.类名.class
  • 2.对象.getClass
  • 3.Class.forName(“类名”);—常用

我们写一个小程序来过一遍

public class CalssTest {

    public static void main(String[] args) throws ClassNotFoundException {
        String str1 = "liu";
        Class class1 = str1.getClass();
        Class class2 = String.class;
        Class class3 = Class.forName("java.lang.String");

        System.out.println(class1 == class2);
        System.out.println(class1 == class3);
        System.out.println(class2 == class3);

    }
}

通过打印的结果我们可以知道我们都是输出同一份字节码,所以结果都是true,我们再来看,我们的Class其实还有一个方法

System.out.println(class1.isPrimitive());
System.out.println(Integer.class == int.class);
System.out.println(int.class == Integer.TYPE);

打印的结果是true,false,true,那这三又是什么意思呢?这个就留给大家猜想一下,应该很容易就知道了的

  • isPrimitive是否是基础类型或者原始类型

那我们的数组是什么类型呢?

宗旨,只要是在源程序中出现的类型,都有各自的Class类型,列入int[],Void等

二.反射的概念

好的。了解了Class类的神奇,我们就可以来看看反射的机制了

反射的概念:反射就是把JAVA类中的各种成分映射成相应的JAVA类,例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分,成员变量,方法,构造方法,包等信息,也用一个个的JAVA类来表示,就像汽车是一个类,汽车中的发动机,变速箱也是一个类,表示JAVA的Class类要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息是用来相应类的实例对象表示,衙门是Field,Method,Contructor,Package等

一个类中的每一个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class来调用各种方法,我们接下来一个个讲

三.Constructor

代表的是某一个类的构造方法,我们用代码来看


public class CalssTest {

    public static void main(String[] args)throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

        //得到String的StringBuffer构造方法
        Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
        String str = constructor.newInstance(new StringBuffer("str"));
        System.out.println(str);
    }
}

通过获得的方法用到上面相同类型的实例,再看newInstance,得到我们设置参数的构造方法,我们再来看下成员变量的反射

四.Fiald

这里我们就模拟了一个类,作为反射的对象


public class FialdClass {

    public int x;
    publicint y;

    public FialdClass(int x,int y) {
        this.x = x;
        this.y = y;
    }
}

那我们实际的操作就是:

public class CalssTest {

    public static void main(String[] args){

        try {
            //首先为x,y 赋值
            FialdClass fClass = new FialdClass(10, 20);
            //反射成员变量
            Field field1 = fClass.getClass().getField("x");
            Field field2 = fClass.getClass().getField("y");
            //指定对象
            System.out.println(field1.get(fClass));
            System.out.println(field2.get(fClass));
        } catch (NoSuchFieldException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (SecurityException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这样我们就可以直接反射到成员变量的值了,这里我们来做一个小练习来测试一下我们成员变量的反射的理解

练习题

  • 假设我们有一个对象中,有许多的String类型成员变量,我们的需求就是把所有对应的字符串内容”b”改成”a”

题目有了我们整理下思路,首先要做的就是定义一个对象咯


public class FialdClass {

    public int x;
    public int y;
    public String str1 = "boy";
    public String str2 = "bbq";
    public String str3 = "hello";

    public FialdClass(int x,int y) {
        this.x = x;
        this.y = y;
    }
    @Override
    public String toString() {
        return "FialdClass [x=" + x + ", y=" + y + ", str1=" + str1 + ", str2="
                + str2 + ", str3=" + str3 + "]";
    }
}

好的,这里有三个string类型的成员变量,那我们该怎么去反射呢?


/**
 * 假设我们有一个对象中,有许多的String类型成员变量,我们
 * 的需求就是把所有对应的字符串内容"b"改成"a"
 * @author LGL
 *
 */
public class CalssTest {

    public static void main(String[] args) throws Exception{

        FialdClass fClass = new FialdClass(5, 10);
        //得到所有的成員变量
        Field[] fields = fClass.getClass().getFields();
        for(Field field : fields){
            //过滤下String类型
            if(field.getType() == String.class){
                //取值
                String oldValue = (String) field.get(fClass);
                String newValue =  oldValue.replace("b", "a");
                field.set(fClass, newValue);
            }
        }
        System.out.println(fClass);
    }
}

这里思路还是比较清晰的,我们一步步的去过滤,最终更改了值

五.Method

如果前面的都是不怎么常用的,那这个我相信你绝对会喜欢,也就是方法,Method,我们来看看怎么用吧!

public class CalssTest {

    public static void main(String[] args) throws Exception{

        String str1 = "abc";
        //反射charAt方法
        Method method = String.class.getMethod("charAt", int.class);
        //作用对象 第几个
        System.out.println(method.invoke(str1, 2));
    }
}

这里我反射的是String的方法charAt,取第二个char,所以打印出来是c

这其实套用了一种专家模式,谁调用了这个数据,谁就是这个专家,现在我们思考一下,如果我们代码是这样写:

System.out.println(method.invoke(null, 2));

这里我们不传对象,而是传一个null进去,那我们知道这个Method是调用什么吗?而纵观之下,只有static修饰的静态方法是不需要对象的,那就说明他调用的是对象了;

在JDK1.4和JDK1.5中,invoke的参数是有些区别的,不过我们现在都1.8了,自然而然这个我们不需要去考虑和追究;

六.反射Main方法

我们现在来思考一下,如何通过反射去执行一个类的Main方法,这就比较好玩了,这里就相当于一个练习题了,而题目就是:

  • 写一个程序,这个程序能够根据用户提供的类名,去执行该类的main方法

而我们的在写这段程序之前,我们就要思考一下了,我们观察main方法:

public static void main(String[] args) {
    //main方法
}

你是否已经发现,这个main方法的参数是一个数组,,如果我们通过反射来调用这个方法,那我们该如何为invoke方法传递参数呢?

在以前的老程序员可能会在纠结JDK1.4和JDK1.5的invoke区别,但是我们倒不用去考虑这些,因为我们的版本是JDK1.8,这样吧,我们先写个需要调用的类:

public class MainClass {

    public static void main(String[] args) {

        for (String arg : args) {

            System.out.println("Main:" + arg);
        }
    }
}

OK,这个类就是打印下数组的值,那我们真正的反射应该怎么去写尼?传统的调用方法是这样的:

MainClass.main(new String[] { "Hello" });

我们反射的话,就不是这么简单了,我们需要这样:

public class CalssTest {

    public static void main(String[] args) throws Exception {

        // MainClass.main(new String[] { "Hello" });
        // 指定类名
        // String className = args[0];
        // 指定Main方法
        Method main = Class.forName("MainClass").getMethod("main",
                String[].class);
        // 调用main方法
        main.invoke(null, (Object) new String[] { "Hello", "LGL" });
    }
}

这里为什么需要强转为Object?不是说好的传递数组吗?吗是因为JDK为了兼容老版本,如果你传递的是一个数组有多个值会进行拆包,我现在声明一个Object就是告诉他我这里只是一个对象,避免对象异常,我们看下输出的结果:

说明我们是调用成功了的;那这里就牵引出数组的反射和Object的关系了,我们继续来看;

七.数组的反射

数组实际上也是一个对象,这点我们一定要清楚,是吧

  • 具有相同维数和元素类型的数组术语同一类型,即具有相同的Class实例对象
  • 代表数组的Class实例对象的getSuperClass()方法返回的父类Object为对象的Class

这里我们以数组为例子就好了,我们来验证一下字节码:

int[] arr1 = { 3 };
int[] arr2 = { 4 };
//true
System.out.println(arr1.getClass() == arr2.getClass());
//[I
System.out.println(arr1.getClass().getName());

第一个为true也就验证了第一条,第二个输出的结果是[I,这个代表的是什么意思呢?[代表的是数组,I代表的是Int类型,也就是这个是int类型的数组

看一下JDK对照文档就行了

接下来,我们来看下数组的反射具体实践是什么样子的!

八.数组反射实践

我们出道题目好了在这样看起来可能更加生动形象一点

  • 题目:怎么得到数组中的元素?

通过查看API文档,我们知道可以反射这个Array

那这个Array要怎么用呢?

假设我现在封装一个打印的方法,这个打印的方法很简单,我定义一个Object参数,你传进来什么,我就打印什么,那要是传递数组,我就一个个的打印,这样的理解应该就比较清晰了,那我们实际的操作你怎样的呢?


public class CalssTest {

    public static void main(String[] args) throws Exception {

        int[] arr = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
        printObject(arr);
    }

    private static void printObject(Object obj) {
        // 拿到字节码
        Class<? extends Object> classs = obj.getClass();
        // 判断是否是数组
        if (classs.isArray()) {
            // 得到长度
            int len = Array.getLength(obj);
            // 取出每一个
            for (int i = 0; i < len; i++) {
                System.out.print(Array.get(obj, i));
            }
        } else {
            System.out.println(obj);
        }
    }
}

但是如何你要问我这个数组的数据类型,我倒是没有获取到,不知为何!!!

好的,比较浅显的反射基础就偷偷的路过了,有兴趣的朋友可以了解下,也可以进群:555974449 一起来探讨探讨!

时间: 2024-12-09 22:24:17

JAVA进阶之旅(二)——认识Class类,反射的概念,Constructor,Fiald,Method,反射Main方法,数组的反射和实践的相关文章

Java多线程基础(二)定时器类:Timer类和TimerTask类

Java多线程基础(二)定时器类:Timer类和TimerTask类 Timer类和TimerTask类是jdk实现定时器功能的早期方法,jdk1.5以前就支持Timer类和TimerTask类.JDK1.5之后引入了新的机制,将在后续博文中研究. 1 指定时间间隔后执行任务 import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TraditionalTimerTest {

Java进阶(三十二) HttpClient使用详解

Java进阶(三十二) HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性.因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入. 一.简介 HttpClient是A

Java 网络编程(二) 两类传输协议:TCP UDP

两类传输协议:TCP,UDP TCP TCP是Transfer Control Protocol(传输控制协议)的简称,是一种面向连接的保证可靠传输的协议. 在TCP/IP协议中, IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一确定Internet上的一台主机. 而TCP层则提供面向应用的可靠的或非可靠的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的. 通过TCP协议传输,得到的是一个顺序的无差错的数据流. 发送方和接收方的成对的两个socket之间

【深入Java虚拟机】之二:Class类文件结构

平台无关性 Java是与平台无关的语言,这得益于Java源代码编译后生成的存储字节码的文件,即Class文件,以及Java虚拟机的实现.不仅使用Java编译器可以把Java代码编译成存储字节码的Class文件,使用JRuby等其他语言的编译器也可以把程序代码编译成Class文件,虚拟机并不关心Class的来源是什么语言,只要它符合一定的结构,就可以在Java中运行.Java语言中的各种变量.关键字和运算符的语义最终都是由多条字节码命令组合而成的,因此字节码命令所能提供的语义描述能力肯定会比Jav

面向对象和面向过程,python中的类class,python中程序的入口——main方法,

1.程序入口,让main显现出来: print(__name__)#__name___是模块中的隐藏字段,当前模块运行的函数名 if __name__ == __main__ __main__() #定义程序入口:Python中main函数默认是隐藏的,main函数在当前运行的模块下,定义程序入口就是让main函数显 #现出来 def __main__(): ....... 2.面向对象和面向过程: 面向对象:使用对象完成任务,不需要在意过程: 3.什么是类:一个类型,定义class来表示一个类

【Java面试题】60 接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类(concrete class)? 抽象类中是否可以有静态的main方法?

接口可以继承接口.抽象类可以实现(implements)接口,抽象类可以继承具体类.抽象类中可以有静态的main方法. 问:  抽象类是否可继承实体类 (concrete class) 答: 抽象类是可以继承实体类,但前提是实体类必须有明确的构造函数. 答案很明确,可以继承.其实从Object就是个实体类,Java的API文档里,每个抽象类的条目里都明确写着直接或间接继承自Object,所以这点是没有疑问的. 关键在于这答案里所说的"前提是实体类必须有明确的构造函数"一句,是什么意思.

java中数组的反射的探究

数组的反射有什么用呢?何时需要使用数组的反射呢?先来看下下面的代码: Integer[] nums = {1, 2, 3, 4}; Object[] objs = nums; //这里能自动的将Integer[]转成Object[] Object obj = nums; //Integer[]当然是一个Object int[] ids = {1, 2, 3, 4}; //Object[] objs2 = ids; //这里不能将int[]转换成Object[] Object obj2 = ids

JAVA深入研究——Method的Invoke方法。

在写代码的时候,发现从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错.虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码. Method的invoke方法 1.先检查 AccessibleObject的override属性是否为true. Accessibl

JAVA深入研究——Method的Invoke方法

http://www.cnblogs.com/onlywujun/p/3519037.html 在写代码的时候,发现Method可以调用子类的对象,但子类即使是改写了的Method,方法名一样,去调用父类的对象也会报错,虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码. Method的invoke方法 1.先检查 AccessibleObject的override属性是否为true. AccessibleObject是Met