• 23_反射


    反射

    能够分析类能力的程序称为反射

    • 在运行中分析类的能力
    • 在运行中查看对象, 例如, 编写一个toString方法供所有类使用
    • 实现通用的数组操作代码
    • 利用Method对象, 这个对象很像C++中的函数指针

    反射是一种功能强大且复杂的机制. 使用它的主要人员是工具构造者, 而不是应用程序员.

    一. 什么是类对象

    • 类的对象: 基于某个类new出来的对象, 也称为实例对象
    • 类对象: 类加载的产物, 封装了一个类的所有信息(类名, 父类, 接口, 属性, 方法, 构造方法)

    二. 获取类对象的方法

    1. 通过类的对象, 获取类对象
      • Student s = new Student();
      • Class c = s.getClass();
    2. 通过类名获取类对象
      • Class c = 类名.class;
    3. 通过静态方法获取类对象[推荐使用]
      • Class = Class.forName("包名.类名");
    //显示类的加载过程, 在Run下的Edit Configurations下配置 -verbose:class: 
    public class TestClass {
    
        public static void main(String[] args) throws Exception{
    
            getClazz();
        }
        public static void getClazz() throws Exception{
            //1. 通过类的对象, 获取类对象
            Person p1 = new Person();
            Class<?> class1 = p1.getClass();
            //System.out.println(class1.toString());
            System.out.println(class1.hashCode());
    
            //2. 通过类名获取类对象
            Class<?> class2 = Person.class;
            System.out.println(class2.hashCode());
    
            //3. 通过静态方法获取类对象[推荐使用]
            Class<?> class3 = Class.forName("com.reflex.demo01.Person");
            System.out.println(class3.hashCode());
    
        }
    }
    
    

    三. 常见操作

    1. 常用方法

    	public String getName()
    	public Package getPackage()
    	public Class<? super T> getSuperclass()
    	public Class<?>[] getInterfaces()
    	public Constructor<?>[] getConstructors()
    	public T newInstance()
    	public Method[] getMethods()
    	public Field[] getField()
        
    

    2. 常见操作

    public class Demo01 {
    
        public static void main(String[] args) throws Exception{
    
            reflect1();
            reflect2();
            reflect3();
    
            Properties properties = new Properties();
            //properties.setProperty("name", "zhangsan");
            //System.out.println(properties.toString());
            invokeAny(properties, "setProperty", new Class[]{String.class, String.class}, "username", "张三");
            System.out.println(properties.toString());
    
            reflect4();
        }
    
        //1. 使用反射获取类的名字, 包名, 父类, 接口
        public static void reflect1() throws Exception{
            //(1) 获取类对象Person
            Class<?> class1 = Class.forName("com.reflex.demo01.Person");
            //getName() 获取类名
            System.out.println(class1.getName());//com.reflex.demo01.Person
            //getPackage() 获取包名
            System.out.println(class1.getPackage().getName());//com.reflex.demo01
            //getSuperClass() 获取父类
            System.out.println(class1.getSuperclass().getName());//java.lang.Object
            //getInterfaces() 获取接口
            Class<?>[] classes = class1.getInterfaces();
            System.out.println(Arrays.toString(classes));//[interface java.io.Serializable, interface java.lang.Cloneable]
            System.out.println(class1.getSimpleName());//Person 只获取类名,不要前缀
            System.out.println(class1.getTypeName());//com.reflex.demo01.Person 获取类名
        }
    
        //2. 使用反射获取类的构造方法, 创建对象
        public static void reflect2() throws Exception{
            //(1) 获取类的类对象
            Class<?> class2 = Class.forName("com.reflex.demo01.Person");
            //(2) 获取类的所有构造方法Constructor
    //        Constructor<?>[] cons = class2.getConstructors();
    //        for (Constructor<?> con : cons) {
    //            System.out.println(con.toString());
    //        }
            //(3) 获取类中无参构造方法
            Constructor<?> con1 = class2.getConstructor();
            Person zhangsan = (Person)con1.newInstance();
            System.out.println(zhangsan.toString());
            Person lisi = (Person)con1.newInstance();
            System.out.println(lisi.toString());
            //简便方法: 类对象.newInstance
            Person wangwu = (Person)class2.newInstance();
            System.out.println(wangwu.toString());
    
            //(4) 获取类中带参构造方法
            Constructor<?> con2 = class2.getConstructor(String.class, int.class);
            Person xiaoli = (Person)con2.newInstance("小李", 17);
            System.out.println(xiaoli.toString());
        }
    
        //3. 使用反射获取类中的方法, 并调用方法
        public static void reflect3() throws Exception{
            //(1) 获取类对象
            Class<?> class3 = Class.forName("com.reflex.demo01.Person");
            //(2) 获取方法Method对象
            //2.1 getMethods() 只能获取公开的方法, 包括从父类继承过来的方法
            //Method[] methods = class3.getMethods();
            //2.2 getDeclaredMethods() 获取类中所有的方法, 包括私有, 默认, 保护的, 不包含继承的方法
            Method[] methods = class3.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println(method.toString());
            }
            //(3) 获取单个方法
            //3.1 run 无参方法
            Method runMethod = class3.getMethod("run");
            //调用方法
            //正常调用 对象.run();
            Person lily = (Person) class3.newInstance();
            runMethod.invoke(lily);
            System.out.println("--------------------------");
            //3.2 toString 有返回值
            Method toStringMethod = class3.getMethod("toString");
            Object result = toStringMethod.invoke(lily);
            System.out.println(result);
            System.out.println("---------------------------");
            //3.3 eat 带参方法
            Method eatMethod = class3.getMethod("eat", String.class);
            eatMethod.invoke(lily, "苹果");
            System.out.println("--------------------------");
    
            //3.4 私有方法
            Method privateMethod = class3.getDeclaredMethod("privateMethod");
            //设置访问权限无效
            privateMethod.setAccessible(true);
            privateMethod.invoke(lily);
            System.out.println("--------------------------");
    
            //3.5 静态方法
            Method staticMethod = class3.getMethod("staticMethod");
            //正常调用 Person.staticMethod
            staticMethod.invoke(null);
    
        }
    
        //4. 使用反射实现一个可以调用任何对象方法的通用方法
        public static Object invokeAny(Object obj, String methodName, Class<?>[] types, Object...args) throws Exception{
    
            //1. 获取类对象
            Class<?> classAll = obj.getClass();
            //2. 获取方法
            Method method = classAll.getMethod(methodName, types);
            //3. 调用
            return method.invoke(obj, args);
    
        }
    
        //5. 使用反射获取类中的属性
        public static void reflect4() throws Exception{
    
            //(1) 获取类对象
            Class<?> class4 = Class.forName("com.reflex.demo01.Person");
            //(2) 获取属性(字段) 公开的字段, 父类继承的字段
            //Field[] fields = class4.getFields();//0 此方法无法获取字段
            //getDeclaredFields() 获取所有的属性, 包括私有, 默认, 保护的, 不包含继承的属性
    //        Field[] fields = class4.getDeclaredFields();
    //        System.out.println(fields.length);
    //        for (Field field : fields) {
    //            System.out.println(field.toString());
    //        }
            //(3) 获取name属性
            Field nameField = class4.getDeclaredField("name");
            nameField.setAccessible(true);
            //(4) 赋值 正常调用是Person person = new Person(); person.name = "张三";
            Person person = (Person)class4.newInstance();
            nameField.set(person, "张三");// person.setName = "张三";
            //(5) 获取值
            System.out.println(nameField.get(person));// person.getName();
    
        }
    
    }
    

    四. 设计模式介绍

    • 什么是设计模式
      • 一套被反复使用, 多数人知晓的, 经过分类编目的, 代码设计经验的总结. 简单理解: 特定问题的固定解决方法
    • 好处:
      • 使用设计模式是为了可重用代码, 让代码更容易被他人理解, 保证代码可靠性, 重用性
    • 在Gof的<<设计模式>>书中描述了23种设计模式

    五. 工厂设计模式

    • 工厂模式主要负责对象创建的问题
    • 开发中有一个非常重要的原则"开闭原则", 对拓展开放, 对修改关闭
    • 可通过反射进行工厂模式的设计, 完成动态的对象创建

    下面是工厂模式的示例

    示例说明: 假设有一个机器上有很多Usb接口, 外部设备通过插入Usb接口就可以运行,比如, 鼠标键盘电风扇等等, 用反射实现添加新的产品无需修改工厂类

    1. 首先创建一个Usb接口
    public interface Usb {
    
        void service();
    }
    
    1. 创建一个鼠标一个键盘类, 且都实现Usb接口
    public class Mouse implements Usb{//鼠标类
        @Override
        public void service() {
            System.out.println("鼠标开始工作了...");
        }
    }
    
    public class KeyBoard implements Usb{//键盘类
        @Override
        public void service() {
            System.out.println("键盘开始工作了");
        }
    }
    
    1. 创建工厂类
    public class UsbFactory {
        public static Usb creatUsb(String type){
            Usb usb = null;
            Class<?> class1;
            try {
                class1 = Class.forName(type);// 获取类对象
                usb = (Usb)class1.newInstance();// 实例化类对象, 就可以调用接口内的方法了
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            return usb;
        }
    }
    
    1. 创建usb.properties文件
    1 = com.reflex.demo03.Mouse
    2 = com.reflex.demo03.KeyBoard
    
    1. 创建测试文件
    public class Demo {
        public static void main(String[] args) throws Exception{
            System.out.println("-----------请选择1鼠标, 2键盘--------");
            //输入要使用的设备
            Scanner input = new Scanner(System.in);
            String choice = input.next();
            //1 = com.reflex.demo03.Mouse
            //2 = com.reflex.demo03.KeyBoard
            // 我们需要把客户输入的数字1, 2转换为为其产品对应的类名
            //创建Properties对象
            Properties properties = new Properties();
            //创建文件字节流
            FileInputStream fis = new FileInputStream("D:\JavaSE\src\com\reflex\demo03\usb.properties");
            //通过properties加载文件(键值对)
            properties.load(fis);
            fis.close();
    
            //调用工厂类里的方法, 参数是properties通过键(1, 2)得到的值(com.reflex.demo03.Mouse, com.reflex.demo03.KeyBoard),也就是类名
            Usb usb = UsbFactory.creatUsb(properties.getProperty(choice));
            if (usb != null){
                System.out.println("接入成功");
                usb.service();// 调用方法
            }else{
                System.out.println("接入失败");
            }
        }
    }
    

    六. 单例设计模式

    • 单例(Singleton): 只允许创建一个该类的对象

    方式1: 饿汉式(类加载时创建, 天生线程安全)

    • 优点: 线程安全
    • 缺点: 生命周期太长, 浪费内存空间
    public class SingleTon {
    
        //1. 创建一个常量
        private static final SingleTon instance = new SingleTon();
        //2. 私有构造方法
        private SingleTon() {
        }
        //3. 通过公开方法返回这个对象
        public static SingleTon getInstance(){
    
            return instance;
        }
    
    }
    

    方式2: 懒汉式(使用时创建, 线程不安全, 加同步)

    • 优点: 生命周期短, 节省内存空间
    • 缺点: 有线程安全问题 解决办法: 使用同步方法或同步代码块
    public class SingleTon2 {
    
        //1. 创造一个对象, 赋值为null
        private static SingleTon2 instance = null;
        //2.私有构造方法
        private SingleTon2() {}
        //3. 通过一个公开的方法, 返回这个对象, 并加同步
        public static SingleTon2 getInstance(){
            if (instance == null){//提高执行效率
                synchronized (SingleTon2.class){
                    if (instance == null){
                        instance = new SingleTon2();
                    }
                }
            }
            return instance;
        }
    }
    

    方式3: 懒汉式(使用时创建, 线程安全)

    • 使用静态内部类的方式
    public class SingleTon3 {
    
        //1. 私有构造方法
        private SingleTon3() {}
    
        //2. 创建静态内部类(不使用时不执行)
        private static class Holder {
            static SingleTon3 instance = new SingleTon3();
        }
    
        //3.通过公开的方法, 返回静态内部类里的静态对象
        public static SingleTon3 getInstance() {
            return Holder.instance;
        }
    
    }
    

    枚举

    • 什么是枚举
      • 枚举是一个引用类型, 枚举是一个规定了取值范围的数据类型
    • 枚举变量不能使用其他的数据, 只能使用枚举中常量赋值, 提高程序安全性
    • 定义枚举使用enum关键字
    • 枚举的本质
      • 枚举是一个终止类, 并继承Enum抽象类
      • 枚举中常量是当前类型的静态常量
    public enum Gender {//性别枚举
        MALE, FEMALE;
    
    }
    
    public enum Season {//季节枚举
        SPRING, SUMMER, AUTUMN, WINTER
    }
    
    public class TestGender {
    
        public static void main(String[] args) {
    
            Gender gender = Gender.MALE;
            System.out.println(gender.toString());//MALE
    
            Season season = Season.SPRING;
            System.out.println(season.toString());//SPRING
        }
    }
    

    枚举和switch语句的使用

    public class demo01 {
    
        public static void main(String[] args) {
    
            //枚举和switch语句的使用
            Season season = Season.WINTER;
            switch (season) {// byte short int char string 枚举
                case SPRING:
                    System.out.println("春天");
                    break;
                case SUMMER:
                    System.out.println("夏天");
                    break;
                case AUTUMN:
                    System.out.println("秋天");
                    break;
                case WINTER:
                    System.out.println("冬天");
                    break;
                default:
                    break;
            }
        }
    }
    

    注解

    • 什么是注解
      • 注解(Annotation)是代码里的特殊标记, 程序可以读取注解, 一般用于替代配置文件
    • 开发人员可以通过注解告诉类如何运行
      • 在Java技术里注解的典型应用是: 可以通过反射技术去得到类里面的注解, 以决定怎么去运行类
    • 常见注解: @Override, @Deprecated
    • 定义注解使用@interface关键字, 注解中只能包含属性
    • 注解的本质是接口

    创建注解类型

    public @interface MyAnnotation {
        //属性(类似方法)
        String name() default "张三";//默认值
        int age() default 20;//默认值
    }
    
    public class Person {
    
        @MyAnnotation
        public void show(){
    
        }
    }
    
    • 注解属性类型
      • String
      • 基本数据类型
      • Class类型
      • 枚举类型
      • 注解类型
      • 以上类型的一维数组
    public @interface MyAnnotation2 {
        //属性
        //字符串类型
        String value();
        //基本类型
        int num();
        //Class类型
        Class<?> class1();
        //枚举类型
        Gender GENDER();
        //注解类型
        MyAnnotation ANNOTATION();
        //ArrayList<String> list();
    }
    
    • 元注解: 用来描述注解的注解
    • @Retention: 用于指定注解可以保留的域
      • RetentionPolicy.CLASS: 注解记录在class文件中, 运行Java程序时, JVM不会保留.这是默认值
      • RetentionPolicy.RUNTIME: 注解记录在class文件中, 运行Java程序时, JVM会保留, 程序可以通过反射获取该注解
      • RetentionPolicy.SOURCE: 编译时直接丢弃这种策略的注释
    • @Target: 指定注解用于修饰类的哪个成员
    1. 创建一个注解PersonInfo
    @Retention(value = RetentionPolicy.RUNTIME)// 这样才能通过反射得到注解信息
    @Target(value = {ElementType.METHOD})// 表示注解只能放在方法上面
    public @interface PersonInfo {
        String name();
        int age();
        String sex();
    }
    
    1. 创建一个Person类, 里面写一个show()方法, 方法上使用注解PersonInfo
    public class Person {
    
        @PersonInfo(name = "张三", age = 19, sex = "男")
        public void show(String name, int age, String sex){
            System.out.println(name + "-----" + age + "-----" + sex);
        }
    
    
    }
    
    1. 使用反射得到注解信息method.getAnnotation
    public class Demo {
    
        public static void main(String[] args) throws Exception{
    
            //1. 获取类对象
            Class<?> class1 = Class.forName("com.reflex.demoAnnotation.Person");
            //2. 获取方法
            Method method = class1.getMethod("show", String.class, int.class, String.class);
            //3. 获取方法上面得注解信息
            PersonInfo personInfo = method.getAnnotation(PersonInfo.class);
            //4. 打印注解信息
            System.out.println(personInfo.name());// 张三
            System.out.println(personInfo.age());// 19
            System.out.println(personInfo.sex());// 男
            //5. 调用方法
            Person person = (Person)class1.newInstance();
            method.invoke(person, personInfo.name(), personInfo.age(), personInfo.sex());// 张三-----19-----男
        }
    }
    
  • 相关阅读:
    魔改版BBR
    termux 开启 sshd
    Basic berkeley socket functions
    mosh
    XOR 加密
    指定so动态链接库连接器
    UTF8 UTF16 之间的互相转换
    MySQL C API概述
    C JAVA你可能不知道的那些编程细节
    虚拟内存
  • 原文地址:https://www.cnblogs.com/MRASdoubleZ/p/14523151.html
Copyright © 2020-2023  润新知