zoukankan      html  css  js  c++  java
  • Java反射基础

    Java作为一门动态的语言,有非常成熟的框架技术以供我们使用,而这一切都离不开Java反射技术的支持。通过Java反射,我们可以动态的创建和使用对象,使用灵活,没有反射机制,就无法实现框架技术。

    Java程序在计算机的三个阶段

    首先,需要了解一下Java程序在计算机的三个阶段,如图所示,图片来源——【零基础 快速学Java】韩顺平 零基础30天学会Java

    [https://www.bilibili.com/video/BV1fh411y7R8?p=1]:

    Java反射机制

    • 通过Java反射机制,程序在执行期间可以借助于 RefelectionAPI 取得任何类的内部信息,如成员变量、构造器、成员方法等,并能够操作对象的属性及方法。反射在设计模式和框架底层都会用到;
    • 加载完类之后,在堆中就产生了一个类的 Class 的对象,这个对象包含了类的完整结构信息,通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构。

    比如:

    Cat.java

    public class Cat {
        public String name = "喵喵";
        public String age = "20";
        public Cat(String name){
            this.name = name;
        }
        public Cat(){}
        public void cry(){
            System.out.println(name + "喵喵叫...");
        }
    }
    

    Cat.class

    public class Cat {
        public String name = "喵喵";
        public String age = "20";
    
        public Cat(String name) {
            this.name = name;
        }
    
        public Cat() {
        }
    
        public void cry() {
            System.out.println(this.name + "喵喵叫...");
        }
    }
    

    Java反射相关的类

    java.lang.Class//代表一个类,Class对象表示某个类加载后在堆中的对象
    java.lang.refelect.Method//代表类的方法,Methon对象表示某个类的方法
    java.lang.refelect.Field//代表类的成员变量,Field对象表示某个类的成员变量
    java.lang.refelect.Constructor//代表类的构造器,Construvtor表示构造器
        //getFiled不能得到私有的属性
    

    反射调用优化

    反射的优点和缺点

    优点:反射可以动态的创建和使用对象,使用灵活,没有反射机制,就无法实现框架技术;

    缺点:使用反射基本是解释执行,对执行速度有影响

    反射调用优化 — — 关闭访问检查

    • Method和Field、Constructor对象都有setAcceseible()方法
    • setAcceseible作用是启动和禁用访问安全检查的开关
    • 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查

    示例代码:

    package com.blue.reflection;
    
    import com.blue.Cat;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Properties;
    @SuppressWarnings({"all"})
    
    public class Reflection2 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            m1();
            m2();
            m3();
        }
        //传统方法来调用hi
        public static void m1(){
            Cat cat = new Cat();
            long start = System.currentTimeMillis();
            for (int i = 0; i < 90000000; i++) {
                cat.hi();
            }
            long end = System.currentTimeMillis();
            System.out.println("m1= "+(end - start));
        }
        //反射机制调用方法hi
        public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            Class cls = Class.forName("com.blue.Cat");
            Object o = cls.newInstance();
            Method hi = cls.getMethod("hi");
    
            long start = System.currentTimeMillis();
            for (int i = 0; i < 90000000; i++) {
                hi.invoke(o);
            }
            long end = System.currentTimeMillis();
            System.out.println("m2= "+(end - start));
        }
    
        //反射调用优化
        public static void m3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            Class cls = Class.forName("com.blue.Cat");
            Object o = cls.newInstance();
            Method hi = cls.getMethod("hi");
            //取消在反射调用方法时的访问检测
            hi.setAccessible(true);
            long start = System.currentTimeMillis();
            for (int i = 0; i < 90000000; i++) {
                hi.invoke(o);
            }
            long end = System.currentTimeMillis();
            System.out.println("m3= "+(end - start));
        }
    }
    
    

    Java Class类分析

    • Class也是类,因此也继承Object类
    • Class类对象不是new出来的,而是系统创建的
    • 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
    • 每个类的实例都会记得自己是由哪个Class类所生成
    • 通过Class可以完整地得到一个类的完整结构,通过一系列的API
    • Class对象是存放在堆的
    • 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码、变量名、方法名、访问权限等)

    Java Class类的继承类与实现接口关系

    Class常用方法

    package com.blue.class_;
    
    import com.blue.Car;
    
    import java.lang.reflect.Field;
    
    /**
     * User: Blueshadow
     * Date&Time: 2021/11/14 20:49
     * 演示Class类常用的方法
     */
    public class class_ {
        public static void main(String[] args) throws Exception {
            String classAllPath = "com.blue.Car";
            //获取到Car类对应的Class对象
            //<?>表示不确定的Java类型
            Class<?> cls = Class.forName(classAllPath);
            //输出cls
            System.out.println(cls);//显示cls对象,是哪个类的Class对象(class com.blue.Car)
            System.out.println(cls.getClasses());//输出运行类型([Ljava.lang.Class;@4554617c)
            System.out.println(cls.getPackage());//得到包名(package com.blue)
            System.out.println(cls.getName());//得到类名(com.blue.Car)
            //通过cls创建对象实例
            Car car = (Car)cls.newInstance();
            System.out.println(car);//(Car{brand='宝马', price=500000, color='red'})
            //通过反射获取属性
            Field brand = cls.getField("brand");
            System.out.println(brand.get(car));//私有属性则会报错
            //通过反射给属性赋值
            brand.set(car,"奔驰");//重新赋值
            System.out.println(brand.get(car));
            //得到所有的属性
            Field[] fields = cls.getFields();
            for (Field f : fields){
                System.out.println(f.getName());//依次输出所有字段属性的名称
                //brand
                //price
                //color
            }
        }
    }
    
    

    获取Class类对象的六种方式

    package com.blue.class_;
    
    import com.blue.Car;
    
    import java.util.Calendar;
    
    /**
     * User: Blueshadow
     * Date&Time: 2021/11/15 16:18
     * 演示得到Class对象的各种方式
     */
    public class GetClass_ {
        public static void main(String[] args) throws ClassNotFoundException {
            //前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出		    ClassNotFoundException。
    		//应用场景:多用于配置文件,读取类全路径,加载类。
            String classAllPath = "com.blue.Car";
            Class<?> cls1 = Class.forName(classAllPath);
            System.out.println(cls1);
            
            //前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高。
    	    //应用场景:多用于参数传递,比如通过反射得到对应构造器对象。
            Class cls2 = Car.class;
            System.out.println(cls2);
            
            //前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
            //应用场景:通过创建好的对象,获取Class对象
            Cat cat = new Cat();
            Class cls3 = cat.getClass();
            System.out.println(cls3);
            
            //前提:通过类加载器来获取到类的Class对象
            //有四种类加载器
            //先得到类加载器
            ClassLoader classLoader = cat.getClass().getClassLoader();
            //通过类加载器得到Class对象
            Class<?> cls4 = classLoader.loadClass(classAllPath);
            System.out.println(cls4);
            
            //基本数据类型
            Class<Integer> integerClass = int.class;
            System.out.println(integerClass);//int
            
            //基本数据类型对应的包装类,可以通过.TYPE 得到Class类对象
            Class<Integer> type = Integer.TYPE;
            System.out.println(type);//int
            
            //输出对象哈希值
            System.out.println(integerClass.hashCode());//1163157884
            System.out.println(type.hashCode());//1163157884
            //说明两者自动进行装箱和拆箱
        }
    }
    
    

    哪些类型有Class对象

    • 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
    • interface接口
    • 数组
    • enum枚举
    • annotation注释
    • 基本数据类型
    • void
    package com.blue.class_;
    
    import java.io.Serializable;
    
    /**
     * User: Blueshadow
     * Date&Time: 2021/11/15 17:06
     * 演示哪些类型有Class对象
     */
    public class AllTypeClass_ {
        public static void main(String[] args) {
            Class<String> cls1 = String.class;//外部类
            Class<Serializable> cls2 = Serializable.class;//接口
            Class<Integer[]> cls3 = Integer[].class;//数组
            Class<float[][]> cls4 = float[][].class;//二维数组
            Class<Deprecated> cls5 = Deprecated.class;//注解
            Class<Thread.State> cls6 = Thread.State.class;//枚举//State是指线程状态
            Class<Void> cls7 = void.class;//void
            Class<Class> cls8 = Class.class;//Class类
    
            System.out.println(cls1);//class java.lang.String
            System.out.println(cls2);//interface java.io.Serializable
            System.out.println(cls3);//class [Ljava.lang.Integer;
            System.out.println(cls4);//class [[F
            System.out.println(cls5);//interface java.lang.Deprecated
            System.out.println(cls6);//class java.lang.Thread$State
            System.out.println(cls7);//void
            System.out.println(cls8);//class java.lang.Class
        }
    }
    
    

    类加载

    基本说明

    反射机制是Java实现动态语言的关键,也就是通过反射实现类动态加载。

    • 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强;
    • 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。

    类加载时机

    • 当类 创建对象时(new)
    • 当子类被加载时
    • 调用类中的静态成员时
    • 通过反射
    package com.blue.classload;
    import java.lang.reflect.Method;
    import java.util.*;
    import java.util.Scanner;
    
    /**
     * User: Blueshadow
     * Date&Time: 2021/11/15 17:33
     */
    public class ClassLoad_ {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            System.out.print("请输入key:");
            String key = scanner.next();
            switch (key){
                case "1":
                    Dog dog = new Dog();//静态加载
                    dog.cry();
                    break;
                case "2":
                    Class<?> person = Class.forName("Person");//加载类
                    Object o = person.newInstance();
                    Method m = person.getMethod("hi");
                    m.invoke(o);
                    System.out.println("ok");
                    break;
                default:
                    System.out.println("do nothing..");
            }
        }
    }
    
    

    反射暴破创建实例

    通过反射创建对象

    • 方式一:调用类中public修饰的无参构造器
    • 方式二:调用类中的指定构造器
    • Class类相关方法
      • newInstance:调用类中的无参构造器,获取对应类的对象
      • getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
      • getDecalaredConstrictor(Class...clazz):根据参数列表,获取对应的所有构造器对象
    • Constructor类相关方法
      • setAccessible:暴破
      • newInstance(Object...onj):调用构造器
    package com.blue;
    
    import java.lang.reflect.Constructor;
    
    /**
     * User: Blueshadow
     * Date&Time: 2021/11/15 21:41
     * 演示通过反射机制创建实例
     */
    public class RefelectCreateInstance {
        public static void main(String[] args) throws Exception {
            //先获取到User类的Class对象
            Class<?> userClass = Class.forName("com.blue.User");
    
            //通过public的无参构造器创建实例
            Object o = userClass.newInstance();
            System.out.println(o);
    
            //通过public的有参构造器创建实例
            /**
             * constructor 就是
             * public User(String name){
             *         this.name = name;
             *     }
             */
            //先得到对应的构造器,再创建实例,传入形参
            Constructor<?> constructor = userClass.getConstructor(String.class);
            Object o1 = constructor.newInstance("王帅");
            System.out.println(o1);
    
            //通过非公有的有参构造器创建实例
            //先得到私有的构造器对象
            Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class);
            declaredConstructor.setAccessible(true);//反射暴破,使用反射可以访问私有构造器
            Object o2 = declaredConstructor.newInstance(100, "王昭君");
            System.out.println(o2);
        }
    }
    
    class User{
        private int age = 18;
        private String name = "王帅";
        public User(){}
        public User(String name){
            this.name = name;
        }
        private User(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    

    通过反射访问类中的属性

    • 根据属性名获取Field对象
    • 暴破
    • 访问
    • 如果是静态属性,则set和get中的参数 o 可以写成null
    package com.blue;
    
    import java.lang.reflect.Field;
    
    /**
     * User: Blueshadow
     * Date&Time: 2021/11/16 19:21
     * 通过反射访问类中的成员
     */
    public class RefelctAccessProperty {
        public static void main(String[] args) throws Exception {
            //得到Student类对应的Class对象
            Class<?> cls = Class.forName("com.blue.Student");
            //创建对象
            Object o = cls.newInstance();//o的运行类型就是Student类型
            System.out.println(o.getClass());
            Field age = cls.getField("age");
            //通过反射来操作属性
            age.set(o,88);//通过反射来操作属性
            System.out.println(o);
            System.out.println(age.get(o));//直接返回属性的值
    
            //使用反射操作静态、私有属性
            //得到Field对象
            Field name = cls.getDeclaredField("name");
            //对 private name 进行暴破
            name.setAccessible(true);
            name.set(o,"blue");
            System.out.println(o);
    
        }
    }
    
    

    通过反射访问类中的方法

    • 根据方法名和参数列表获取Method方法对象
    • 获取对象
    • 暴破
    • 访问
    • 注意:如果是静态方法,则invoke的参数o,可以写成null
    package com.blue;
    
    import java.lang.reflect.Method;
    
    /**
     * User: Blueshadow
     * Date&Time: 2021/11/16 20:33
     * 演示通过反射调用方法
     */
    public class RefelctAccessMethod {
        public static void main(String[] args) throws Exception {
            //得到Class对象
            Class<?> cls = Class.forName("com.blue.Boos");
            //创建一个对象
            Object o = cls.newInstance();
            //调用public的方法
    //        Method hi = cls.getMethod("hi",String.class);
            //得到Method对象
            Method hi = cls.getDeclaredMethod("hi",String.class);
            //调用
            hi.invoke(o,"blue");
    
    
            //调用private方法
            //获取到方法对象
            Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);
            //因为say方法是私有的,所以需要暴破磁能使用
            say.setAccessible(true);
            System.out.println(say.invoke(o,18,"blue",'男'));
    
            //因为say方法是static,所以可以...可以给对象参数传值为空
            System.out.println(say.invoke(null,200,"李四",'女'));
    
            //返回值问题
            //在反射中,如果方法有返回值,统一返回Object对象
            Object returnValue = say.invoke(null, 300, "王五", '男');
            System.out.println(returnValue.getClass());//运行类型和方法中定义返回类型是一样的
        }
    }
    
    class Boos{
        public int age;
        private static String name;
        public Boos(){}
        private static String say(int n,String s,char c){
            return n + ""+s+""+c;
        }
        public void hi(String s){
            System.out.println("hi "+s);
        }
    }
    
  • 相关阅读:
    前端之css网页布局等相关内容-54
    前端之css属性设置等相关内容-53
    前端之css选择器等相关内容-52
    前端之HTML标签等相关内容-51
    前端之HTML基础等相关内容-50
    数据库之mysql索引增删改查等相关内容-48
    累了就拥抱下自己
    平静地生活
    良心远大于失败
    独处
  • 原文地址:https://www.cnblogs.com/nanfengashuai/p/15568860.html
Copyright © 2011-2022 走看看