reflect反射部分的完整,玩转java框架的灵魂《万物皆对象》

SqlServer:不支持java语言

模块之间的关系——耦合度(高内聚、低耦合)

类的加载过程:

定义:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制,拿到字节码文件即可获取对应的类的代表对象


	1. .java文件------->(编译).class文件---->加载到方法区----->对class文件进行封装

**反射----解析类,**通过字节码对象来获取实例对象的过程(反射一定是发生在运行时期的,编译期间是不可能发生发射现象的)

《在JAVA眼中,万物皆都对象》
1.Class —>代表类的类(产生的对象就是一个具体的类)(字节码对象

	Class 类的实例表示正在运行的 Java 应用程序中的类和接口。

2.Field—>代表类的属性的类(产生的对象就是一个具体的属性)

	Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。 

3.Method—>代表方法的类(产生的对象就是一个具体的方法)

	Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 

4.ConStructor—>代表构造方法的类(产生的对象就是一个具体的构造方法)

	Constructor 提供关于类的单个构造方法的信息以及对它的访问权限

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的
对象是类的具体实现

获取类的字节码对象:三种方式

获取字节码对象的方式一: 类名.class—>任何一个类都能得到对应的字节码对象(加载进了内存)

String,StringBuilder,StringBuffer都是最终类,没有子类
class类也是Object类的子类,class产生的事指定类型的类的类对象


        //获取字节码对象
        //String类的字节码对象
        Class<String> clz = String.class;
        System.out.println(clz);//class java.lang.String

        //接口中的字节码对象
        Class<List> clz1 = List.class;
        System.out.println(clz1);//interface java.util.List

        //基本类型的字节码对象   不在任何包下的对象
        Class<Integer> clz2 = int.class;
        System.out.println(clz2);//int

获取字节码对象方式二: 通过对象获取字节码文件,已经加载进了内存并且创建了对象 对象.getClass().

		静态资源,可以直接通过类名,进行调用,可以用来获取代表类的对象,通过类的对象,可以获取对象的属性
		static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。 
		static Class<?> forName(String name, boolean initialize, ClassLoader loader) 
 
		 //基本类型的字节码对象  基本类型的class对象的泛型是包装类
        //因为里面接收的是对象的类型,int不能创建对象,包装类才有对象
        Class<Integer> in=int.class;

        //2.对象获取字节码对象   方法的强转类型----返回是固定类型,类型不一致就需要进行强转
        Class<String> clz3 = (Class<String>) "abc".getClass();
        System.out.println(clz3);//class java.lang.String
        //3.通过类对象来创建对象  通过使用类对象进行获取字节码文件的时候,需要进行类型的判断
        //当加载的是类的时候,才能进行相关的class字节码,因为只有类才有字节码对象
        //所以当报错找不到类的时候,只有可能是类名灭有进行写对
        Class<?> c2 = forName("java.lang.reflect.Constructor");
        System.out.println(c2);

获取字节码文件方式三: 通过加载获取类的全限定类名进行加载进内存,从而产生字节码文件,有个字节码文件之后才有字节码的对象(还没加载到内存当中,需要将.java文件加载进入内存) Class.forName(“全限定类名”)

为什么要是全限定类名的呢?不能保证不同的包的文件中的类名存在雷同的现象




        //3.必须写入路径类名,因为java中不同的包可能会出现同一个类名,所以不能只写类名
        Class<List> clz4= (Class<List>) Class.forName("java.util.List");
        System.out.println(clz4);//interface java.util.List

原始数据类型一般是基本数据类型:int double…

相关方法的的应用都是获取相关的属性,相关的方法:

//获取String类的class对象
        Class<String> clz = String.class;

        //获取所有的实现的接口或者继承的街路口
        Class<?>[] cs = clz.getInterfaces();
        for (Class<?> c : cs) {
            System.out.println(cs);
        }

        //获取相关的包名
        Package pack = clz.getPackage();
        System.out.println(pack);

        //获取String类的父类
        Class<? super String> super1 = clz.getSuperclass();
        System.out.println(super1);

        //判断是否是内部类
        System.out.println(new Object().getClass().isAnonymousClass());

        //判断是否是基本数据类型---->也就是原始数据类型
        System.out.println(int.class.isPrimitive());

获取实例对象的方式:

		- 获取字节码文件对象(代表类的类)
		- 使用字节码对象获取构造方法:对象的产生是由构造方法构建的
		- 使用构造方法创建对象并返回该对象(构造方法:有参构造和无参构造,有部分类没有无参构造方法)
		- 操作该对象

getConstructor()—getDeclaredConstructor()的区别:

	- getConstructor方法的获取的是公共的方法,接口中和公开构造方法
	- getDeclaredConstructor()方法可以获取到类中的公共的方法和私有的方法,常理都可以进行获取操作
	- 存在部分的语言访问权限指令:
	- setAccessible(true):暴力破解,进行暴力反射能够越级访问,那个区域需要暴力访问,那么就需要就应该调用该方法,避免出现存在私有的方法或者默认的访问权限

getField()—getDeclaredField()的区别;

	- getField()获取的是公共的属性,接扣中或者类中的公共属性
	- getDeclaredField()可以获取所有的声明的属性,公共和私有的都可以进行获取,一般都调用这个方法。
	- - 存在部分的语言访问权限指令:
	- setAccessible(true):暴力破解,进行暴力反射能够越级访问,那个区域需要暴力访问,那么就需要就应该调用该方法,避免出现存在私有的方法或者默认的访问权限,能够进行私有属性的修改
	- set(Object obj,object obj)  和  get(Object obj )---->可以用来获取或者修改类中的属性
	- field.get(obj)   filed.set(obj,obj)
 //通过字节码对象来获取实例对象
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        //获取字节码对象
        Class<String> clz=String.class;

        //通过字节码对象获取实例对象
        //执行无参构造的方法创建无参对象  有可能抛出的异常是不合法的异常,受访问权限的yuanyin
        String s=clz.newInstance();
        //创建的对象返回的是空串在底层的实现
        System.out.println(s);

        //先获取有参构造
        //参数类型需要传入的是以字节码的形式来传入才能找到的数据
        //因为底层的String是由java进行提供的,获取的是原来已经在底层的公共方法
        //所以使用普通的getConstructor方式进行获取即可
        Constructor<String> c1=clz.getConstructor(String.class);
        //通过构造方法获取实例对象  c1已经确定了对象的对象的类型,所以接收的时候不需要进行强制类型的转换
       //执行有参构造并且返回实例对象
        String s2=c1.newInstance("颤三");
        System.out.println(s2);


        //通过反射来获取INteger的对象

        //1.先获取Integer的字节码对象----》Integer类没有无参构造方法
        Class<Integer> in=Integer.class;
        //2.通过字节码对象来获取构造方法
        Constructor<Integer> in2=in.getConstructor(int.class);
        //3.通过构造方法来获取对象
        //3.1对象的创建,都是来自于构造方法的初始化进行创建
        Integer in3=in2.newInstance(123);
        //打印出Integer对象--->实现自动拆箱的过程,在1.5
        System.out.println(in3);
        //通过实现自动拆箱的过程
        System.out.println(in3.intValue());





    }

暴力反射的案列

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

        Class<String> clz=String.class;

        //通过字节码对象获取构造方法
        Constructor c1=clz.getDeclaredConstructor(char[].class,boolean.class);
        //通过构造方法创建对象,  关闭语言访问检查机制,对此构造方法
        c1.setAccessible(true);
        //将字符序列装欢成字符串的形式
        String s= (String) c1.newInstance(new char[]{'1','2'},true);
        //打印d对象
        System.out.println(s);

    }
}


反射中的强制修改属性的方法:

public class ClassDemo4 {

    //通过暴力反射改变类的值
    //知名反射是强大的,没有阻碍进行数据访问权限
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        //获取私有化属性
        Class<String> clz=String.class;

        //对象才能进行属性的访问
        //获取构造方法   属性也是一个对象,由自己的hashcode值
        Field field=clz.getDeclaredField("hash");
        //对象存在hashcode值
        String str="abc";
        //获取hashcode的值
        System.out.println(str.hashCode());

        //进行hashcode的值的修改
        //实现暴力反射
        field.setAccessible(true);

        //进行值的修改   Set(Object obj,int val)
        //第一个参数  表示要修改的是哪个对象,后面表示的是修改的值
        field.set(str,1234);

        //获取hashcode的值   field表是hash属性的对象,通过对象可以确定hash的归属
        System.out.println(field.get(str));
        System.out.println(str.hashCode());
    }

}



Method对象的使用:  方法的调用使用  invoke(Object obj,...)
public class ClassDemo5 {

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

        //底层的方法的反射结构
        //获取字节码文件对象
        Class<String> clz=String.class;

        //获取指定的方法
        //第一个参数  方法的名字
        //第二个参数   方法参数的字节码文件对象
        Method m=clz.getDeclaredMethod("charAt", int.class);
        //前提是要获取到该对象,动态代理模式中常用
        String str="asdjkagdsg";

        //执行方法,基于对应的类进行执行方法
        //第一个参数  调用该方法的对象
        //第二个参数  方法中需要传递的参数
        char c= (char) m.invoke(str,6);
        System.out.println(c);
    }
}


反射的缺点:

	- 打破封装原则:所有的东西都可以继续获取,私有化也可以进行获取
	- 跳过泛型的类型检测

缺点的实现:

反射在编译期跳过了编译检测数据类型

public class ClassDemo7 {

    //实现反射跳过类型检测的现象

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

        //规定类型是string类型
        List<String> list=new ArrayList<>();
        list.add("abc");

        //奇迹出现
        //获取上面对象的字节码文件
        Class<List> clz= (Class<List>) list.getClass();
        //获取List集合中的方法
        Method m=clz.getDeclaredMethod("add", Object.class);
        //进行数据元素的添加
        m.invoke(list,123);
        m.invoke(list,true);
        System.out.println(list);
        //原本在创建对象的时候,指定的类型是String,但是其他类型也能进行添加,说明跳过了类型的检测机制
        //编译的时候h会有泛型不会进行类型的检测,如果不符合数据类行就会检错
        //在运行期间反射改变存储数据元素类型,直接跳过编译检测,也是就是说在反射
    }

}

案列——实现克隆方法(clone()):

无参构造的属性克隆操作:

package cn.tedu.reflect01;

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

public class ReflectDemoTest {

    //实现底层的clone方法

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

        //开始测试
        Person p=new Person();
        p.setAge(19);
        p.setName("张元宝");

        //进行克隆对象
        Person person= (Person) clone(p);
        //打印结果数据
        System.out.println(p);
        System.out.println(person);


    }

    public static Object clone(Object obj) throws IllegalAccessException, InvocationTargetException, InstantiationException {

        //创建原来对象字节码对象
        Class<Object> clz= (Class<Object>) obj.getClass();
        //获取实例对象,通过字节码对象实现无参构造对象
        //至少获取到了一个构造方法
        Constructor c=clz.getDeclaredConstructors()[0];
        //通过构造方法创建对象
        Object obj1=c.newInstance();

        //通过字节码文件对象获取所有的属性
        Field[] field=clz.getDeclaredFields();

        //进行遍历属性,将属性每个进行一一复制的操作
        for (Field f : field) {
            //暴力反射,比买按属性没有被复制过去
            f.setAccessible(true);
            //获取原来对象的属性值
            Object value=f.get(obj);
            //进行属性的复制操作
            f.set(obj1,value);
        }
            //属性复制结束后,进行新对象的返回
        return obj1;
    }
}


class Person{
    private String name;
    private int age;

    public Person(){}


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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

带参构造的属性的克隆操作:

package cn.tedu.reflect01;

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

public class ReflectDemoTest2 {
    //实现底层的clone方法  升级版的形成,可以进行克隆带参数的构造方法

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

        //进行测试
        Student s=new Student();
        s.setName("弘历");
        s.setAge(20);



        //进行克隆的复制
        Student s1= (Student) clone(s);

        System.out.println(s);
        System.out.println(s1);
    }

    public static Object clone(Object obj) throws IllegalAccessException, InvocationTargetException, InstantiationException {

        //创建原来对象字节码对象
        Class<Object> clz= (Class<Object>) obj.getClass();
        //获取实例对象,通过字节码对象实现无参构造对象
        //至少获取到了一个构造方法
        Constructor c=clz.getDeclaredConstructors()[0];
        //通过构造方法创建对象
       // Object obj1=c.newInstance();

        //获取构造方法上的所有的参数类型的对象表示   按照声明的类型返回变量的类型,同时获取参数的对象字节码文件对象
        Class[] parms=c.getParameterTypes();
        //提供数组进行存储有参构造参数的初始化值---->提供数组存储函数中中参数的舒适化的值
        Object[] os=new Object[parms.length];

        //遍历数组,遍历的是参数列表的数组
        for (int i = 0; i < parms.length; i++) {
            //进行类型的判断
            if (parms[i].isPrimitive()){
                //判断是否是元数据类型(基本数据类型)
                if (parms[i]==byte.class||parms[i]==short.class||
                parms[i]==int.class){
                    os[i]=0;
                }else if (parms[i]==char.class){
                    os[i]='\u0000';
                }else if (parms[i]==long.class){
                    os[i]=0L;
                }else if (parms[i]==float.class){
                    os[i]=0.0f;
                }else if (parms[i]==double.class){
                    os[i]=0.0;
                }else{
                    os[i]=false;
                }
            }else{
                //引用数据类型
                os[i]=null;
            }
        }

        //获取构造函数参数对象
        //执行构造函数并返回对象   构造方法中存放的是数组的对象,里面存放的是构造函数的参数
        //传递的时候传递的是数组的对象,在构造函数中进行体现,传入参数的类型
        Object obj2=c.newInstance(os);
        //通过字节码文件对象获取所有的属性
        Field[] field=clz.getDeclaredFields();
        //进行遍历属性,将属性每个进行一一复制的操作
        for (Field f : field) {
            //暴力反射,比买按属性没有被复制过去
            f.setAccessible(true);
            //获取原来对象obj的属性值——->获取原来的传递过来的对象进行获取它的属性值
            Object value=f.get(obj);
            //进行属性的复制操作 新对象进行值的设定操作
            f.set(obj2,value);
        }
        //属性复制结束后,进行新对象的返回
        return obj2;
    }
}

class Student{

    private String name;
    private int age;

    public Student(){}

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值