JAVA 基础 day17 反射

本文深入解析Java反射机制,涵盖类对象获取、构造方法与实例化、方法与字段操作,以及反射的优缺点。同时,介绍如何利用反射实现设计模式如简单工厂模式和单例模式,探讨枚举和注解的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反射Reflect

在这里插入图片描述
类对象,把类当成对象来看
在这里插入图片描述

获取类的类对象

1.通过对象获取 对象.getClasss()

  		Student student = new Student("chichi", 18, "shen");
        Class<? extends Student> aClass = student.getClass();

2.类名.class

Class<Student> studentClass = Student.class;

3.静态方法 Class.forname(“类的全名称”); 推荐 耦合性低,灵活,传入字符串,编译时即使没有该类也不会报错

 Class<?> aClass1 = Class.forName("week4.day17.Student");

类对象在内存中只有一个

获取类的一些属性
public static void main(String[] args) throws Exception{
        Class<?> class1 = Class.forName("week4.day17.Student");
        System.out.println(class1.getName());
        System.out.println(class1.getSimpleName());
        System.out.println(class1.getPackage().getName());
        // 获得父类
        Class<?> superclass = class1.getSuperclass();
        // 获得接口
        Class<?>[] interfaces = class1.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface.getName());
        }

获取构造方法类,创造实例对象

// 获取构造方法
        // 获得全部构造方法
        Constructor<?>[] constructors = class1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        // 获得单个构造
        // 1.有参构造
        Constructor<?> constructor1 = class1.getConstructor(String.class, int.class, String.class);
        Student s1 =(Student)constructor1.newInstance("chichi", 18, "nan");
        // 2.无参构造
        Constructor<?> constructor2 = class1.getConstructor();
        Student s2= (Student) constructor2.newInstance();
        // 使用无参简单创建对象的简单方式
        Student s3 = (Student) class1.newInstance();

获取方法

		Class<?> class1 = Class.forName("week4.day17.Student");
        // 获取全部方法
        // getMethods()获取全部的公开方法,包括继承来的公开方法,没有私有默认,保护方法
        // getDeclaredMethods 获取类中全部方法,不包括继承来的
        Method[] methods = class1.getMethods();
        Method[] declaredMethods = class1.getDeclaredMethods();
        //获取单个方法
        Method show = class1.getMethod("show");
        // 1.调用无参方法
        Student s1 = (Student) class1.newInstance();
        show.invoke(s1);
        // 2.调用有参方法
        Method show1 = class1.getMethod("show", String.class);
        show1.invoke(s1,"chichi");
        // 3. 调用有返回值的方法
        Constructor<?> constructor = class1.getConstructor(String.class, int.class, String.class);
        Student chichi = (Student) constructor.newInstance("chichi", 18, "99");
        Method getName = class1.getMethod("getName");
        String name = (String) getName.invoke(chichi);
        // 4.调用私有方法
        Method privateMethod = class1.getDeclaredMethod("privateMethod");
        //    设置访问权限失效
        privateMethod.setAccessible(true);
        privateMethod.invoke(s1);
        // 5.调用静态方法
        Method staticMethod = class1.getMethod("staticMethod");
        staticMethod.invoke(null );

有返回值的
在这里插入图片描述
获取属性

		Class<?> class1 = Class.forName("week4.day17.Student");
        // 获取全部字段
        Field[] declaredFields = class1.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        // 获取单个字段
        Field name = class1.getDeclaredField("name");
        // 设置访问权限
        name.setAccessible(true);
        Student s1= (Student) class1.newInstance();
        // 设置字段
        name.set(s1,"chichi");
        // 获取字段
        System.out.println(name.get(s1));

反射的优缺点

内省

在这里插入图片描述
属性:如果类中包含了getXxx,setXxx,isXxx开头的方法,表示类中有xxx属性,这个属性并不指字段

属性操作方式:
1.反射属性(暴力破解,需设置字段访问权限)
2.反射setXxx方法(麻烦,还需提前了解属性的名称及类型)
3.内省 不需要事先知道属性的类型,属性的名称

// 不需要事先知道属性的名称和他的类型
public static void main(String[] args) throws Exception{
        Class<?> class1 = Class.forName("week4.day17.Student");
        Student student  = (Student) class1.newInstance();
        // 获得属性名称,以便创建描述符
        BeanInfo beanInfo = Introspector.getBeanInfo(class1);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//        for (PropertyDescriptor pd : propertyDescriptors) {
//            System.out.println(pd.getName());
//        }
        // 获得描述符
        // 1.根据遍历结果通过下标获得
//        PropertyDescriptor pdAge = propertyDescriptors[0];
        // 2.根据遍历结果新建描述符对象
        PropertyDescriptor pdAge=new PropertyDescriptor("age",class1);
        //获得属性类型,以便赋值
        System.out.println(pdAge.getPropertyType());
        // 赋值
        Method writeMethod = pdAge.getWriteMethod();
        writeMethod.invoke(student,18);//实际调用setAge方法
        //获得值
        Method readMethod = pdAge.getReadMethod();
        System.out.println(readMethod.invoke(student));//实际调用getAge方法
    }

设计模式

在这里插入图片描述

简单工厂模式

在这里插入图片描述
在这里插入图片描述
使用配置文件,反射进而达到开闭原则,配置文件使用properties文件

单例模式

在这里插入图片描述
1.饿汉式:类一加载,对象就初始化
缺点:生命周期长,浪费空间 优点:线程安全

懒汉式:

public class SingleCon {
    // 懒汉模式
    // 1.私有化构造方法
    private static boolean f = false;
    private SingleCon(){
        if (f){
        	// 防止使用反射强行破解单例模式(当然不能完全防止)
            // 当被反射会调用构造方法 抛出运行时异常
            throw new RuntimeException("不要使用反射破解");
        }
    }
    // 在类内部创建一对象
    private static volatile SingleCon instance;//防止指令重排
    // 公开方法返回对象
    public static SingleCon getInstance(){
        //有线程创建了对象了,其他线程就不要在阻塞等着锁再来进行判断了,直接返回
        if (instance==null) {//目的:提高效率,双重检查
            synchronized (SingleCon.class){//防止多个线程判断为空时创建,造成多个对象
                if (instance==null){
                	//创建实例
                    instance=new SingleCon();
                    f=true;
                }
            }
        }
        return instance;
    }

}

外层判断避免多个线程在变量已经实例化后还需要拿锁,当锁住并创建后,其他线程就不在需要拿锁了
volatile 禁止指令重排

好处:声明周期短,节省空间 缺点:线程不安全,必须使用同步解决

3.静态内部类写法
最好的写法

// 使用静态内部类
    // 1.私有化构造方法
    private SingleStatic(){}
    // 2.在静态类内部
    static class Inner{
        private static SingleStatic instance = new SingleStatic();
    }
    // 3.通过公开方法返回这个对象
    public static SingleStatic getInstance(){
        return Inner.instance;
    }

优点: 1.安全 2.生命周期短,节省空间

枚举

锁定了取值范围的数据类型
枚举其实就是静态常量
注意问题
1.枚举举常量直接用逗号隔开,最后分号可写可不写,如果后边有代码,必须加分号
2. 枚举只能包含私有构造方法,属性和普通方法,必须写在常量后边

public enum Gender {
    MAN,WOMEN;//常量写在最前边
    private Gender(){}
    int s=8;
    public void show(){
    }
}

注解

反编译:本质上就是接口,其属性就是公开抽象方法

在这里插入图片描述
属性的类型只能是:1.基本类型 2.String 3.Class类型 4.枚举类型 5.注解类型
以及上述类型的一维数组

只能包含公开属性,定义属性时,可以给默认值,没给默认值得使用时必须要赋值

如果注解中使用value属性名,当只要value一个属性需要赋值时可以不用写属性名,直接赋值

元注解

在这里插入图片描述
给注解的注解,放在自定义注解的上边

使用反射获取注解信息

//运行程序时,JVM会保留,这样才能通过反射获取到注释
// 否则获取不到注解
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfo {
    String name() default "chichi";
    int age() default 18;
    String gender();
    String value();
}

public class Person {
    private String name;
    private int age;
    private String gender;
    public Person() {
    }
    // 特殊 只有value一个属性需要赋值时,可不写属性名
    @PersonInfo(gender = "男", value = "value")
    public void printInfo(){
        try {
            // 获取类属性
            Class<?> class1 = Class.forName("week4.day17.annotation.Person");
            // 获取方法
            Method printInfo = class1.getMethod("printInfo");
            // 获取注解
            PersonInfo annotation = printInfo.getAnnotation(PersonInfo.class);
            // 获取注解属性
            System.out.println(annotation.name());
            System.out.println(annotation.age());
            System.out.println(annotation.gender());
            System.out.println(annotation.value());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

注解应用
// 让反射读取到
@Retention(RetentionPolicy.RUNTIME)
// 应用到方法上
@Target(ElementType.METHOD)
public @interface ContentType {
        String value();
}

// 判断方法是否被注解修饰
if (method.isAnnotationPresent(ContentType.class)){
ContentType annotation = method.getAnnotation(ContentType.class);
String value = annotation.value();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值