Java基础篇之反射机制详解

目录
  • 1、反射概述
    • 1.1什么是反射
    • 1.2、反射能干什么
  • 2、解剖类
    • 2.1反射构造方法
      • 2.1.1反射无参的构造函数
      • 2.1.2反射“一个参数”的构造函数
      • 2.1.3反射“多个参数”的构造函数
      • 2.1.4反射“私有”的构造函数
      • 2.1.5反射得到类中所有的构造函数
    • 2.2反射类中的方法
    • 2.3反射类中的属性字段

思考:在讲反射之前,先思考一个问题,java中如何创建一个对象,有哪几种方式?

Java中创建对象大概有这几种方式:

  • 1、使用new关键字:这是我们最常见的也是最简单的创建对象的方式
  • 2、使用Clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去
  • 3、使用反序列化:当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象

上边是Java中常见的创建对象的三种方式,其实除了上边的三种还有另外一种方式,就是接下来我们要讨论的 “反射”

1、反射概述

1.1什么是反射

反射就是把Java类中的各个部分,映射成一个个的Java对象,拿到这些对象后可以做一些事情。

既然说反射是反射Java类中的各个组成部分,所以说咱们得知道一个类中有哪儿些部分?

例如,一个类有:成员变量,方法,构造方法,等信息,利用反射技术咱们可以把这些组成部分映射成一个个对象。

1.2、反射能干什么

说完反射的概念后,咱们说一下反射能干什么?

一般来说反射是用来做框架的,或者说可以做一些抽象度比较高的底层代码,反射在日常的开发中用到的不多,但是咱们还必须搞懂它,因为搞懂了反射以后,可以帮助咱们理解框架的一些原理。所以说有一句很经典的话:反射是框架设计的灵魂。现在说完这个可能还不太能理解,不急,等下说完一个快速入门的例子后,应该会稍微有点感觉

1.3、怎么得到想反射的类

刚才已经说过,反射是对一个类进行解剖,想解剖一个东西,前提是首先你得拿到这个东西,那么怎么得到咱们想解剖的类呢?

首先大家要明白一点,咱们写的代码是存储在后缀名是 .java的文件里的,但是它会被编译,最终真正去执行的是编译后的 .class文件。Java是面向对象的语言,一切皆对象,所以java认为 这些编译后的 class文件,这种事物也是一种对象,它也给抽象成了一种类,这个类就是Class,大家可以去AIP里看一下这个类

所以拿到这个类后,就相当于拿到了咱们想解剖的类,那怎么拿到这个类?

看API文档后,有一个方法forName(String className); 而且是一个静态的方法,这样咱们就可以得到想反射的类了

到这里,看Class clazz = Class.forName("com.cj.test.Person");这个应该有点感觉了吧

Class.forName("com.cj.test.Person");因为这个方法里接收的是个字符串,字符串的话,我们就可以写在配置文件里,然后利用反射生成我们需要的对象,这才是我们想要的。很多框架里都有类似的配置

2、解剖类

我们知道一个类里一般有构造函数、方法、成员变量(字段/属性)这三部分组成

翻阅API文档,可以看到

Class对象提供了如下常用方法:

  • public Constructor getConstructor(Class<?>…parameterTypes)
  • public Method getMethod(String name,Class<?>… parameterTypes)
  • public Field getField(String name)
  • public Constructor getDeclaredConstructor(Class<?>…parameterTypes)
  • public Method getDeclaredMethod(String name,Class<?>… parameterTypes)
  • public Field getDeclaredField(String name)

这些方法分别用于帮咱们从类中解剖出构造函数、方法和成员变量(属性)。

然后把解剖出来的部分,分别用Constructor、Method、Field对象表示。

2.1反射构造方法

2.1.1反射无参的构造函数

可以看到 默认的无参构造方法执行了

从上边的例子看出,要想反射,首先第一步就是得到类的字节码

所以简单说一下得到类的字节码的几种方式

  • (1)、Class.forName("com.cj.test.Person"); 这就是上边我们用的方式
  • (2)、对象.getClass();
  • (3)、类名.class;

2.1.2反射“一个参数”的构造函数

2.1.3反射“多个参数”的构造函数

2.1.4反射“私有”的构造函数

注意:在反射私有的构造函数时,用普通的clazz.getConstructor()会报错,因为它是私有的,所以提供了专门反射私有构造函数的方法 clazz.getDeclaredConstructor(int.class);//读取私有的构造函数,用这个方法读取完还需要设置一下暴力反射才可以

c.setAccessible(true);//暴力反射

2.1.5反射得到类中所有的构造函数

2.2反射类中的方法

package com.cj.test;

import java.util.Date;

public class Person {

	public Person(){
		System.out.println("默认的无参构造方法执行了");
	}

	public Person(String name){
		System.out.println("姓名:"+name);
	}

	public Person(String name,int age){
		System.out.println(name+"="+age);
	}

	private Person(int age){
		System.out.println("年龄:"+age);
	}

	public void m1() {
		System.out.println("m1");
	}

	public void m2(String name) {
		System.out.println(name);
	}

	public String m3(String name,int age) {
		System.out.println(name+":"+age);
		return "aaa";
	}

	private void m4(Date d) {
		System.out.println(d);
	}

	public static void m5() {
		System.out.println("m5");
	}

	public static void m6(String[] strs) {
		System.out.println(strs.length);
	}

        public static void main(String[] args) {
		System.out.println("main");
	}

}
package com.cj.test;

import java.lang.reflect.Method;
import java.util.Date;
import org.junit.Test;

public class Demo2 {

	@Test//public void m1()
	public void test1() throws Exception{
		Class clazz = Class.forName("com.cj.test.Person");
		Person p = (Person)clazz.newInstance();
		Method m = clazz.getMethod("m1", null);
		m.invoke(p, null);
	}
	@Test//public void m2(String name)
	public void test2() throws Exception{
		Class clazz = Person.class;
		Person p = (Person) clazz.newInstance();
		Method m = clazz.getMethod("m2", String.class);
		m.invoke(p, "张三");
	}
	@Test//public String m3(String name,int age)
	public void test3() throws Exception{
		Class clazz = Person.class;
		Person p = (Person) clazz.newInstance();
		Method m = clazz.getMethod("m3", String.class,int.class);
		String returnValue = (String)m.invoke(p, "张三",23);
		System.out.println(returnValue);
	}
	@Test//private void m4(Date d)
	public void test4() throws Exception{
		Class clazz = Person.class;
		Person p = (Person) clazz.newInstance();
		Method m = clazz.getDeclaredMethod("m4", Date.class);
		m.setAccessible(true);
		m.invoke(p,new Date());
	}
	@Test//public static void m5()
	public void test5() throws Exception{
		Class clazz = Person.class;
		Method m = clazz.getMethod("m5", null);
		m.invoke(null,null);
	}
	@Test//private static void m6(String[] strs)
	public void test6() throws Exception{
		Class clazz = Person.class;
		Method m = clazz.getDeclaredMethod("m6",String[].class);
		m.setAccessible(true);
		m.invoke(null,(Object)new String[]{"a","b"});
	}
	@Test
	public void test7() throws Exception{
		Class clazz = Person.class;
		Method m = clazz.getMethod("main",String[].class);
		m.invoke(null,new Object[]{new String[]{"a","b"}});
	}
}

*****注意:看下上边代码里test6和test7的invoke方法里传的参数和其他的有点不一样

这是因为 jdk1.4和jdk1.5处理invoke方法有区别

1.5:public Object invoke(Object obj,Object…args)

1.4:public Object invoke(Object obj,Object[] args)

由于JDK1.4和1.5对invoke方法的处理有区别, 所以在反射类似于main(String[] args) 这种参数是数组的方法时需要特殊处理

启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数个数不对的问题。

上述问题的解决方法:

  • (1)mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

这种方式,由于你传的是一个数组的参数,所以为了向下兼容1.4的语法,javac遇到数组会给你拆开成多个参数,但是由于咱们这个Object[ ] 数组里只有一个元素值,所以就算它拆也没关系

  • (2)mainMethod.invoke(null,(Object)new String[]{"xxx"});

这种方式相当于你传的参数是一个对象,而不是数组,所以就算是按照1.4的语法它也不会拆,所以问题搞定

编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了

对上边的描述进行一下总结:在反射方法时,如果方法的参数是一个数组,考虑到向下兼容问题,会按照JDK1.4的语法来对待(JVM会把传递的数组参数拆开,拆开就会报参数的个数不匹配的错误)

解决办法:防止JVM拆开你的数组

  • 方式一:把数组看做是一个Object对象
  • 方式二:重新构建一个Object数组,那个参数数组作为唯一的元素存在。

2.3反射类中的属性字段

package com.cj.test;

import java.util.Date;

public class Person {

	public String name="李四";
	private int age = 18;
	public static Date time;

	public int getAge() {
		return age;
	}

	public Person(){
		System.out.println("默认的无参构造方法执行了");
	}

	public Person(String name){
		System.out.println("姓名:"+name);
	}

	public Person(String name,int age){
		System.out.println(name+"="+age);
	}

	private Person(int age){
		System.out.println("年龄:"+age);
	}

	public void m1() {
		System.out.println("m1");
	}

	public void m2(String name) {
		System.out.println(name);
	}

	public String m3(String name,int age) {
		System.out.println(name+":"+age);
		return "aaa";
	}

	private void m4(Date d) {
		System.out.println(d);
	}

	public static void m5() {
		System.out.println("m5");
	}

	public static void m6(String[] strs) {
		System.out.println(strs.length);
	}

	public static void main(String[] args) {
		System.out.println("main");
	}

}
package com.cj.test;

import java.lang.reflect.Field;
import java.util.Date;
import org.junit.Test;

public class Demo3 {
	//public String name="李四";
	@Test
	public void test1() throws Exception{
		Class clazz = Person.class;
		Person p = (Person)clazz.newInstance();
		Field f = clazz.getField("name");
		String s = (String)f.get(p);
		System.out.println(s);

		//更改name的值
		f.set(p, "王六");
		System.out.println(p.name);
	}
	@Test//private int age = 18;
	public void test2() throws Exception{
		Class clazz = Person.class;
		Person p = (Person)clazz.newInstance();
		Field f = clazz.getDeclaredField("age");
		f.setAccessible(true);
		int age = (Integer)f.get(p);
		System.out.println(age);

		f.set(p, 28);
		age = (Integer)f.get(p);
		System.out.println(age);
	}
	@Test//public static Date time;
	public void test3() throws Exception{
		Class clazz = Person.class;
		Field f = clazz.getField("time");
		f.set(null, new Date());
		System.out.println(Person.time);
	}
}

以上就是自己对Java中反射的一些学习总结,欢迎大家留言一起学习、讨论

看完上边有关反射的东西, 对常用框架里的配置文件是不是有点思路了

上边是Spring配置文件里的常见的bean配置,这看起来是不是可以用反射很轻易的就可以实现:解析xml然后把xml里的内容作为参数,利用反射创建对象。

以上所述是小编给大家介绍的Java基础篇之反射机制详解,希望对大家有所帮助。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java中的反射机制基本运用详解

    目录 Java中的反射机制基本运用 1.什么是反射(reflect) 2.反射机制提供的功能 3.反射->获取类对象 4.反射->利用无参构造实例化对象 5.反射->利用有参构造实例化对象 6.反射->调用无参方法 7.反射->调用有参方法 8.反射->访问私有方法 9.反射->类加载路径 总结 Java中的反射机制基本运用 看完反射可以了解一下注解 注解annotation://www.jb51.net/article/221276.htm 1.什么是反射(re

  • Java反射机制的简单讲解

    🌱1. 什么是反射机制? 首先大家应该先了解两个概念,编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行,JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制 简单说,反射机制值得是程序在运行时能够获取自身的信息.在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息. 🌱2. java反射机制提供了什么功能? 在运行时能够判断任意一个对象所属的类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的成员变量和方法 在运行时调用任一对象的方法 在运行时创建新类对象 🌱3.new和反射创建有什么区别呢? ne

  • Java基础篇之反射机制示例详解

    目录 一.什么是反射: 二.反射的原理: 三.反射的优缺点: 四.反射的用途: 五.反射机制常用的类: 六.反射的基本使用: 1.获得Class:主要有三种方法: 2.判断是否为某个类的示例: 3.创建实例:通过反射来生成对象主要有两种方法: 4.通过反射获取构造方法并使用: 5.获取成员变量并调用: 6.获取成员方法并调用: 7.反射main方法: 8.利用反射创建数值: 9.反射方法的其他使用--通过反射运行配置文件内容: 10.反射方法的其他使用--通过反射越过泛型检查: 总结 一.什么是

  • Java反射机制基础详解

    目录 1.什么是Java反射机制? 2.反射机制原理 3.Class类介绍 3.1.Class类基本介绍 3.2.Class类对象的获取方法 Class.forname() 3.3 .可以获取Class对象的类型 4.java反射的作用? 5.反射API主要类 6.Java反射的优缺点 7.反射调用的优化方法 8.反射的基本使用例子 9.反射开放权限操作 总结 1.什么是Java反射机制? 在程序运行中动态地获取类的相关属性,同时调用对象的方法和获取属性,这种机制被称之为Java反射机制 下面给

  • java反射机制最详解

    目录 java反射机制 什么是反射? 反射的功能: 反射常用类: 1.Class枚举类 2.Constructor构造器 3.Method方法类 4.Field变量类 反射运行指示图 通过反射获取对象 总结 java反射机制 什么是反射? 在java开发中有一个非常重要的概念就是java反射机制,也是java的重要特征之一.反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力,通过反射可以调用私有方法和私有属性,大部分框架也都是运用反射原理的

  • Java中反射机制和作用详解

    前言 很多刚学Java反射的同学可能对反射技术一头雾水,为什么要学习反射,学习反射有什么作用,不用反射,通过new也能创建用户对象. 那么接下来大师就带你们了解一下反射是什么,为什么要学习反射? 下面我们首先通过一个实例来说明反射的好处: 方法1.不用反射技术,创建用户对象,调用sayHello方法 1.1 我们首先创建一个User类 package com.dashi; /** * Author:Java大师 * User对象,包含用户的id和姓名以及sayHello方法 */ public

  • 详解Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制.通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以,这是一种动态获取类的信息以及动态调用对象方法的能力. 想要使用反射机制,就必须要先获取到该类

  • Java基础篇之反射机制详解

    目录 1.反射概述 1.1什么是反射 1.2.反射能干什么 2.解剖类 2.1反射构造方法 2.1.1反射无参的构造函数 2.1.2反射“一个参数”的构造函数 2.1.3反射“多个参数”的构造函数 2.1.4反射“私有”的构造函数 2.1.5反射得到类中所有的构造函数 2.2反射类中的方法 2.3反射类中的属性字段 思考:在讲反射之前,先思考一个问题,java中如何创建一个对象,有哪几种方式? Java中创建对象大概有这几种方式: 1.使用new关键字:这是我们最常见的也是最简单的创建对象的方式

  • Java动态代理和反射机制详解

    反射机制 Java语言提供的一种基础功能,通过反射,我们可以操作这个类或对象,比如获取这个类中的方法.属性和构造方法等. 动态代理:分为JDK动态代理.cglib动态代理(spring中的动态代理). 静态代理 预先(编译期间)确定了代理者与被代理者之间的关系,也就是说,若代理类在程序运行前就已经存在了,这种情况就叫静态代理 动态代理 代理类在程序运行时创建的代理方式.也就是说,代理类并不是在Java代码中定义的,而是在运行期间根据我们在Java代码中的"指示"动态生成的. 动态代理比

  • Java基础之垃圾回收机制详解

    一.GC的作用 进行内存管理 C语言中的内存,申请内存之后需要手动释放:一旦忘记释放,就会发生内存泄漏! 而Java语言中,申请内存后会由GC来释放内存空间,无需手动释放 GC虽然代替了手动释放的操作,但是它也有局限性: 需要消耗更多的资源: 没有手动释放那么及时: STW(Stop The World)会影响程序的执行效率 二.GC主要回收哪些内存 (1)堆:主要回收堆中的内存 (2)方法区:需要回收 (3)栈(包括本地方法栈和JVM虚拟机栈):不需要回收,栈上的内存什么时候释放是明确的(线程

  • Java中的反射机制详解

    Java中的反射机制详解 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧! 一,先看一下反射的概念: 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接.但是反射使用不当会成本很高! 看概念很晕的,继续往下

  • Java 反射机制详解及实例

    Java 反射机制详解及实例 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!            一,先看一下反射的概念: 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接.但是反射使用不当会成本很高

  • Java基础之序列化与反序列化详解

    目录 1.什么是序列化与反序列化? 2.Java如何实现序列化和反序列化? 3.如何自定义序列化和反序列化呢? 4.writeObject和readObject方法 5.serializable接口 1.什么是序列化与反序列化? 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是XML等格式.而字节的或XML编码格式可以还原完全相等的对象.这个相反的过程又称为反序列

  • Java基础面试题之volatile详解

    目录 1.volatile保证可见性 1.1.什么是JMM模型? 1.2.volatile保证可见性的代码验证 1.2.1.无可见性代码验证 1.2.1.volatile保证可见性验证 2.volatile不保证原子性 2.1 什么是原子性? 2.2 不保证原子性的代码验证 2.3 volatile不保证原子性的解决方法 2.3.1 方法1:使用synchronized 2.3.2 方法1:使用JUC包下的AtomicInteger 3.volatile禁止指令重排 3.1 什么是指令重排? 3

  • Java基础之动态代理Cglib详解

    一.前言 经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码.cglib执行速度略大于jdk,所以比较适合单例模式.另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中.如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常.spring默认使用jdk动态代理,如果类没有接口,则使用cglib. 二.服务 package proxy.cglib; /** * @Description: <br/>

  • Java基础之switch分支结构详解

    一.基本语法 二.流程图 1.画出 swtich 出流程 2.案例说明流程图 三.快速入门 案例:Switch01.java 请编写一个程序,该程序可以接收一个字符,比如:a,b,c,d,e,f,g a 表示星期一,b 表示星期二 - 根据用户的输入显示相应的信息.要求使用 switch 语句完成 代码: /* 案例:Switch01.java 请编写一个程序,该程序可以接收一个字符,比如:a,b,c,d,e,f,g a表示星期一,b表示星期二 - 根据用户的输入显示相应的信息.要求使用 swi

随机推荐