Java基础学习day_17 反射

反射(reflect)

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

反射的相关类

在这里插入图片描述

获取类的类对象(三种方式)

//1通过对象获取类对象
Student student =new Student("学子",18,"男");
Class<? extends Student> aClass = student.getClass();
System.out.println("类对象的hashCode:"+aClass.hashCode());
//2通过类获取类对象,类.class
Class<Student> studentClass = Student.class;
System.out.println("类对象的hashCode:"+studentClass.hashCode());
//3通过Class方法,Class.forName(包名.类名)推荐使用这个,耦合性低,灵活
Class<?> aClass1 = Class.forName("qf.test.test01.Student");
System.out.println("类对象的hashCode:"+aClass1.hashCode());

获取类对象的信息

//创建类对象
Class<?> class1 = Class.forName("qf.test.test01.Student");
//得到带有包名的类名
System.out.println(class1.getName());
//得到类名
System.out.println(class1.getSimpleName());
//得到包名
System.out.println(class1.getPackage());
System.out.println(class1.getPackage().getName());
//遍历接口
Class<?>[] interfaces = class1.getInterfaces();
for (Class<?> anInterface : interfaces) {
    System.out.println(anInterface.getName());
}
//得到父类
Class<?> superclass = class1.getSuperclass();
System.out.println(superclass.getName());

获取构造类,创建实例对象

//遍历构造方法
Constructor<?>[] constructors = class1.getConstructors();
for (Constructor<?> constructor : constructors) {
    //含有参数
    System.out.println(constructor);
    //只有包名 构造方法名 没有参数
    System.out.println(constructor.getName());
}

//获取单个有参构造方法并实例化对象
Constructor<?> constructor = class1.getConstructor(String.class, int.class, String.class);
Student student = (Student)constructor.newInstance("haha", 12, "男");
System.out.println(student.toString());
//获取单个无参构造方法并实例化对象
Constructor<?> constructor1 = class1.getConstructor();
Student student1 = (Student) constructor1.newInstance();
System.out.println(student1.toString());
//获取单个无参构造方法并实例化对象
Student student2=(Student) class1.newInstance();
System.out.println(student2.toString());

获取类方法

所有方法,本类方法,有参方法,无参方法,静态方法,私有方法,通用方法

//获取类对象
Class<?> class1= Class.forName("qf.test.test01.Student");
//获取方法
//获取所有的方法:getMethods():公开的所有的方法,包括继承的公开方法
//但是没有私有,默认,保护的方法。
Method[] methods = class1.getMethods();
for (Method method : methods) {
    System.out.println(method);
}
//获取类中的所有的方法,包含私有,默认,保护的方法,但是
//不包括继承的方法。
Method[] declaredMethods = class1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.println(declaredMethod.toString());
}
//获取单个无参方法
Method methodShow = class1.getMethod("show");
//创建实例对象
Student student =(Student) class1.newInstance();
//使用方法
methodShow.invoke(student);
//获取有参方法
Method show = class1.getMethod("show", String.class,int.class);
show.invoke(student,"上海",12);
//获取私有的方法
Method privateMethod = class1.getDeclaredMethod("privateMethod");
//设置访问权限无效
privateMethod.setAccessible(true);
privateMethod.invoke(student);
//获取静态方法
Method staticMethod = class1.getMethod("staticMethod");
staticMethod.invoke(null);

通用方法:可以调用任何对象的任何方法

//参数:实例对象,方法名,参数类型,实参
public static void invokeAny(Object object,String methodName,Class[] paramsType,Object ...args)throws Exception{
    //获取类对象
    Class<?> class1 = object.getClass();
    //获取方法
    Method method = class1.getMethod(methodName, paramsType);
    //调用方法
    method.invoke(object, args);
}

测试:

Properties properties=new Properties();
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"天气","晴天");
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"地址","北京");
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"小狗","汪汪");
invokeAny(properties, "setProperty",new Class[]{String.class,String.class},"小猫","晴天");
System.out.println(properties.toString());

获取类属性

获取属性类,赋值,获取属性值

//创建类对象
Class<?> class1 = Class.forName("qf.test.test01.Student");
//获取所有的属性
Field[] fields = class1.getDeclaredFields();
//遍历
for (Field field : fields) {
    System.out.println(field.toString());
}
//获取单个属性
Field name = class1.getDeclaredField("name");
//创建实例对象
Student student =(Student)class1.newInstance();
//设置访问权限
name.setAccessible(true);
System.out.println(name.get(student));
Field age = class1.getDeclaredField("age");
//设置访问权限
age.setAccessible(true);
age.set(student, 14);
System.out.println(age.get(student));

反射优点和缺点

在这里插入图片描述

反射实现插件(见我的其他博客文章)

内省

在这里插入图片描述
对属性进行操作,有三种
1.反射属性,但是这种暴力反射的行为,不符合封装的特性(所以尽量不要这么使用)

//获取单个属性
Field name = class1.getDeclaredField("name");
//创建实例对象
Student student =(Student)class1.newInstance();
//设置访问权限
name.setAccessible(true);
System.out.println(name.get(student));
Field age = class1.getDeclaredField("age");
//设置访问权限
age.setAccessible(true);
age.set(student, 14);
System.out.println(age.get(student));

2.通过获取getXxx,setXxx方法,去获取或者修改属性的值,但是这种方法很麻烦,而且还必须知道数据的类型。

//创建类对象
Class<?> class1 = Class.forName("qf.test.test08.Person");
//获取getName方法
Method getName = class1.getMethod("getName");
//获取setName方法
Method setName = class1.getMethod("setName", String.class);
//实例化对象
Person person = (Person) class1.newInstance();
//设置属性
setName.invoke(person, "张三");
//获取属性
System.out.println(getName.invoke(person));

3.(重要)内省的方式对属性进行操作。属性:如果类中包含了有返回值的无参getXxx,无返回值的有参SetXxx,boolean类型的isXxx方法,表示类中有xxx属性
在这里插入图片描述
首先我们可以通过遍历,得知属性名的类型和属性名

//创建类对象
Class<?> class1 = Class.forName("qf.test.test04.Person");
//根据Introspector工具类创建实体类信息
BeanInfo beanInfo = Introspector.getBeanInfo(class1);
//通过实体类信息创建属性描述符
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//遍历属性
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
    //获取属性名
    System.out.println(propertyDescriptor.getName());
    //获取属性的类型
    System.out.println(propertyDescriptor.getPropertyType());
}

对属性进行操作

//创建类对象
Class<?> class1 = Class.forName("qf.test.test04.Person");
//实例化对象
Person person = (Person)class1.newInstance();
//创建属性描述符
PropertyDescriptor pdName=new PropertyDescriptor("name", class1);
PropertyDescriptor pdAge = new PropertyDescriptor("age", class1);
PropertyDescriptor pdGender = new PropertyDescriptor("gender", class1);
//获得写方法
Method writeName = pdName.getWriteMethod();
writeName.invoke(person, "张三");
Method writeAge = pdAge.getWriteMethod();
writeAge.invoke(person, 19);
Method writeGender = pdGender.getWriteMethod();
writeGender.invoke(person, "男");
//获得读方法
System.out.println(pdName.getReadMethod().invoke(person));
System.out.println(pdAge.getReadMethod().invoke(person));
System.out.println(pdGender.getReadMethod().invoke(person));

设计模式

在这里插入图片描述

简单工厂模式

在这里插入图片描述

单例模式(全局只有一个)

在这里插入图片描述

饿汉式:类一加载,对象就初始化。
缺点:生命周期长,浪费空间。
优点:线程安全

懒汉模式:

public class TestSingleTon {
    //防止暴力反射,但是还是能被破解的
    private static boolean flag=false;
    //在类内部创建一个对象
    private static volatile TestSingleTon testSingleTon;
    //私有化构造方法
    private TestSingleTon(){
        if(flag){
            throw new RuntimeException("不能暴力反射破解我的构造方法");
        }
    }

    public static TestSingleTon getInstance(){
        if(testSingleTon==null){//目的:提高效率,double check 双重检查
            synchronized (TestSingleTon.class){
                if(testSingleTon==null){
                    //创建实例
                    testSingleTon=new TestSingleTon();
                    flag=true;
                }
            }
        }
        return testSingleTon;
    }
}

好处:节省空间,生命周期短。缺点:线程不安全

懒汉模式就一定是单例模式吗?不是的,通过暴力反射就可以让它不是单例模式

//创建实例对象
TestSingleTon single=TestSingleTon.getInstance();
System.out.println(single.hashCode());
//创建类对象
Class<?> SingleTon = Class.forName("qf.test.test06.TestSingleTon");
//获得私有构造方法
Constructor<?> testSingleTon = SingleTon.getDeclaredConstructor();
//设置访问模式
testSingleTon.setAccessible(true);
//获得私有属性
Field flag = SingleTon.getDeclaredField("flag");
//设置访问模式
flag.setAccessible(true);
//修改私有属性
flag.set(single, false);
//flag.set(testSingleTon.newInstance(),"false");
TestSingleTon singleTon1=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon1.hashCode());

TestSingleTon singleTon2=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon2.hashCode());

TestSingleTon singleTon3=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon3.hashCode());

TestSingleTon singleTon4=(TestSingleTon) testSingleTon.newInstance();
System.out.println(singleTon4.hashCode());

结果地址都是不一样的。

460141958
356573597
1735600054
21685669
2133927002

new对象时,分四步1,new 2,dup 3,invokespecial 4,a_store,因为指令有优化,可能会造成指令重排,比如这样1,2,4,3,对单线程没有影响,但是对多线程有影响,所以用volitail修饰符。

单列模式的第三种写法(重要)

静态内部类写法
优点:安全 生命周期短,节省空间

public class SingleTon {
    //私有化构造方法
    private SingleTon(){
    }
    //在静态内部类中创建对象
    public static class Single{
        private static final SingleTon singleTon=new SingleTon();
    }
    //公开方法返回这个对象
    public static SingleTon getInstance(){
        return Single.singleTon;
    }
}

枚举

在这里插入图片描述

public enum Gender {
    MALE,FEMALE;
    private String name="哈哈哈";

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

反编译一下
在这里插入图片描述所以说,枚举其实是静态常量

1.枚举常量之间用逗号隔开,最后分后可写也可不写,如果后面有代码,必须加分号。
2.枚举包含私有的构造方法,属性,和普通方法。必须写在常量的后面。

注解(给程序读的)

注释是给程序员看的。
在这里插入图片描述
@Deprecated 过时的

只能包含公开属性,定义属性时,可以给默认值。如果注解中只有一个属性名,且是value属性名,可以不用@注解(value=xxx)。可以@注解(xxx)

String name() default "张三"

属性的类型:1.基本类型,2.String类型,3.枚举类型 4.注解类型 5 Class类型 以及上述类型的一维数组。

@Target:注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包

在这里插入****图片描述注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Annotation1 {
    String name() default "哈哈";
    int value();
    int age();
    String gender()default "男";
}

反射得到注解

@Annotation1(value=12,age=24)
public void show(){
    System.out.println("我要得到注解信息");
    try {
        //创建类对象
        Class<?> class1 = Class.forName("qf.test.test09.Person");
        //获取方法
        Method show = class1.getMethod("show");
        //获取注解
        Annotation1 annotation = show.getAnnotation(Annotation1.class);
        //输出属性
        System.out.println(annotation.age());
        System.out.println(annotation.gender());
        System.out.println(annotation.name());
        System.out.println(annotation.value());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
}

用class文件,反编译一下,
在这里插入图片描述

注解其实就是接口,里面的属性就是公开抽象方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值