javaSE--7反射机制

一、反射机制

1.反射机制的作用

通过java语言中的反射机制可以操作字节码文件(类似于黑客,可以读和写字节码文件)

可以操作代码片段(class文件)

反射机制相关的类在java.lang.reflect.*;包下

2.反射机制相关的类有哪些?

java.lang.Class:整个字节码,代表一个类型

java.lang.reflect.Method:字节码中的方法字节码

java.lang.reflect.Constructor:字节码中的构造方法字节码

java.lang.reflect.Field:字节码中的属性字节码

例:

//Class
public class User{
	//Field
	int no;
	//Constructor
	public User(){}
	public User(int no){
		this.no = no;
	}
	//Method
	public void setNo(int no){
		this.no = no;
	}
	public void getNo(int no){
		return no;
	}
}

二、获取Class 的三种方式

要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?

1.Class.forName()

Class c = Class.forName(“完整类名带包名”)

​ ①静态方法

​ ②方法的参数是一个字符串

​ ③字符串需要的是一个完整类名

​ ④完整类名必须带有包名,java.lang包也不能省略

Class c1 = Class.forName("java.lang.String");//c1代表String.class文件,或代表String类型
Class c2 = Class.forName("java.util.Date");//c2代表Date类型
Class c3 = Class.forName("java.lang.Integer");//c3代表Integer类型
Class c4 = Class.forName("java.lang.System");//c4代表System类型

重点:

如果只希望一个类的静态代码块执行,其他代码一律不执行,可以使用:

Class.forName(“完整类名”);

这个方法的执行会导致类加载,类加载时,静态代码块执行

2.getClass()

Class c = 引用.getClass()

java 中任何一个对象都有一个方法:getClass()

 //getClass()
        String s = "abc";
        Class x = s.getClass();//x代表String.Class字节码文件,x代表String类型
        System.out.println(c1 == x);//true(==判断的是对象的内存地址)

在这里插入图片描述

3. .Class属性

Class c = 任何类型.class;

java语言中任何一种类型,抱愧基本数据类型,都有.class属性

Class z = String.class;//z代表String类型
Class k = Date.class;
Class f = int.class;
Class e = double.class;

三、通过反射实例化对象

获取到Class文件能干什么?

​ 通过Class的newInstance()方法来实例化对象

​ 注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参数构造存在才可以

try {
            //通过反射机制,获取Class,通过Class来实例化对象
            Class c = Class.forName("bean.User");
            try {

                //newInstance()方法会调用User这个类的无参数构造方法,完成对象的创建
                //重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的
                Object obj = c.newInstance();
                System.out.println(obj);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

四、通过读属性文件实例化对象

验证反射机制的灵活性

​ 非常灵活,符合OCP开闭原则:对扩展开放,对修改关闭
在这里插入图片描述

五、获取类路径下文件的绝对路径

FileReader reader = new FileReader("reflect/classinfo.properties");

这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根

接下来是一种通用路径

注意:

​ 使用一下通用方式的前提是:这个文件必须在类路径下

什么是类路径?在src下的都是类路径下

src是类的根路径

String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();

解释:

​ Thread.currentThread():当前线程对象

​ getContextClassLoader():是线程对象的方法,可以获取到当前线程的类加载器对象

​ getResource():【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源

//获取一个文件的绝对路径
        String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo.properties").getPath();

        FileReader reader = new FileReader(path);
        Properties pro = new Properties();
        pro.load(reader);
        reader.close();
        //通过key获取value
        String className = pro.getProperty("className");
        System.out.println(className);

直接以流的形式返回:

 InputStream reader = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("classinfo.properties");
        Properties pro = new Properties();
        pro.load(reader);
        reader.close();
        //通过key获取value
        String className = pro.getProperty("className");
        System.out.println(className);

六、资源绑定器

java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容

使用以下方式,属性配置文件xxx.properties必须放到类路径下

资源绑定器:

​ 只能绑定xxx.properties文件,且这个文件必须在类路径下,文件筐扩展名也必须是properties

​ 写路径时,路径后的扩展名不能写

ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");
String className = bundle.getString("className");
System.out.println(className);

七、类加载器(了解即可)

1.什么是类加载器?

专门负责加载类的命令/工具----ClassLoader

2.JDK中自带了3个类加载器

启动类加载器、扩展类加载器、应用类加载器

3.如何进行加载?

首先会通过启动类加载器加载:专门加载rt/.jar中都是JDK最核心的类库

如果加载不到,会通过扩展类加载器加载:专门加载ext/*jar中的类

如果加载不到,会通过应用类加载器加载:专门加载classpath中的类

4.双亲委派机制

java中为了保证类加载的安全,使用了双亲委派机制

​ 优先从启动类加载器中加载,称为“父”

​ “父”无法加载到,再从扩展类加载器中加载,称为“母”

​ 双亲委派,如果都加载不到,才会考虑从应用类加载器中加载,知道加载到为止

八、获取Field

Field翻译为字段,就是属性/成员

        //获取整个类
        Class studentClass = Class.forName("bean.Student");

        //bean.Student
        String className = studentClass.getName();
        System.out.println("完整类名:" + className);

        //Student
        String simpleName = studentClass.getSimpleName();
        System.out.println("简类名:" + simpleName);

        //获取类中所有的public修饰的Field
        Field[] fields = studentClass.getFields();
        System.out.println(fields.length);//测试数据中只有一个元素
        //取出这个Field
        Field f = fields[0];
        //取出这个Field的名字
        String filedName = f.getName();
        System.out.println(filedName);

        //获取所有的Field
        Field[] fs = studentClass.getDeclaredFields();
        System.out.println(fs.length);//4

        System.out.println("--------------");

        //遍历
        for (Field field:fs){

            //获取属性的修饰符列表
            //返回的修饰符是一个数字,每个数字是修饰符的代号
            int i = field.getModifiers();//修饰符可能有多个所以是复数
            System.out.println(i);

            //可以将代号数字转换成字符串吗?
            String modFieldString = Modifier.toString(i);
            System.out.println(modFieldString);

            //获取属性的类型
            Class fieldType = field.getType();//返回Class
            String fName = fieldType.getName();
            System.out.println(fName);

            //获取属性的名字
            System.out.println(field.getName());

1.反编译Field(以后不会用,了解即可)

        //创建这个是为了拼接字符串
        StringBuilder s = new StringBuilder();


        Class studentClass = Class.forName("bean.Student");

        s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() +" {\n");

        Field[] fields = studentClass.getDeclaredFields();

        //对属性进行循环
        for (Field field : fields){
            s.append("\t");
            s.append(Modifier.toString(field.getModifiers()));
            s.append(" ");
            s.append(field.getType().getSimpleName());
            s.append(" ");
            s.append(field.getName());
            s.append(";\n");
        }

        s.append("}");
        System.out.println(s);

2.通过反射机制访问对象属性【必须掌握】

①怎么通过反射机制访问一个java对象的属性?

给属性赋值set

获取属性的值get

获取属性依靠名字区分

//不使用反射机制怎么访问对象属性?
Student s = new Student();
//给属性赋值
s.no = 111;
//读属性值
System.out.println(s.no);

//使用反射机制怎么访问?
Class studentClass = Class.forName("bean.Student");
Object obj = studentClass.newInstance();//obj就是Student对象

//获取no属性(根据属性名称获取Field)
Field noField = studentClass.getDeclaredField("no");

//给obj对象(Student对象)的no赋值
noField.set(obj,2222);//给obj对象的no属性赋值

//读取属性的值
System.out.println(noField.get(obj));

②私有属性的访问

//可以访问私有属性吗?
Field nameField = studentClass.getDeclaredField("name");

//打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会)
//此时,在外部也可以访问private
nameField.setAccessible(true);

//给name属性赋值
nameField.set(obj,"jackson");
//获取name属性的值
System.out.println(nameField.get(obj));

3.可变长参数【不属于Field】

int… args :这就是可变长参数

语法:类型…(注意:一定是三个点)

①可变长度参数要求的参数个数是:0~N个

②可变长度参数在参数列表中必须在最后一个位置上,且可变长度参数只能有一个

③可变长度参数可当做一个数组看待

九、反射Method(不需掌握)

//获取类
        Class userServiceClass = Class.forName("bean.Service");
        //获取所有的Method(包括私有的)
        Method[] methods = userServiceClass.getDeclaredMethods();
        System.out.println(methods.length);//2

        //遍历Method
        for (Method method : methods){
            //获取修饰符列表
            System.out.println(Modifier.toString(method.getModifiers()));

            //获取方法的返回值类型
            System.out.println(method.getReturnType().getSimpleName());
            //获取方法名
            System.out.println(method.getName());
            //方法的修饰符列表(一个方法的参数可能会有多个)
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes){
                System.out.println(parameterType.getSimpleName());
            }
        }

反编译Method(了解)

类似反编译Field

十、反射机制调用方法(重点,必须掌握)

要素:

​ 对象、方法名、实参列表、返回值

//不使用反射机制怎么调用方法?
        //创建对象
        UserService userService = new UserService();
        //调用方法
        boolean loginSuccess = userService.login("admin","123");
        System.out.println(loginSuccess);//true

        //使用反射机制来调用一个对象的方法
        Class userServiceClass = Class.forName("bean.UserService");
        //创建对象
        Object obj = userServiceClass.newInstance();
        //获取Method
        Method loginMethod = userServiceClass.getDeclaredMethod("login",String.class,String.class);
        //调用方法
        Object retValue = loginMethod.invoke(obj,"admin","123");
        System.out.println(retValue);//true

反射机制:让代码具有通用性,可变化的内容都是写到配置文件中,将来修改配置文件之后,创建的对象就不一样了,调用的方法也不同了,但java代码不需要任何改动

十一、反射机制创建对象(重点,掌握)

//不使用反射机制怎么创建对象
        Vip v1 = new Vip();
        Vip v2 = new Vip(110,"zhangsan","2001-10-11",true);

        //使用反射机制
        Class c = Class.forName("bean.Vip");
        //调用无参数构造方法
        Object obj = c.newInstance();
        System.out.println(obj);

        //调用有参数构造方法
        //第一步:现货区这个有参数的构造方法
        Constructor con = c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
        //第二步:调动构造方法new对象
        Object newObj = con.newInstance(110,"jackon","1990-10-11",true);
        System.out.println(newObj);

十二、获取父类和父接口(重点)

//String举例
        Class stringClass = Class.forName("java.lang.String");
        //获取String的父类
        Class superClass = stringClass.getSuperclass();
        System.out.println(superClass.getName());//java.lang.Object

        //获取String类实现的所有接口(一个类可以实现多个接口)
        Class[] interfaces = stringClass.getInterfaces();
        for (Class in : interfaces){
            System.out.println(in.getName());
        }

十三、注解(Annotation)

是一种引用数据类型,变异之后也是生成xxx.class文件

1.如何自定义注解?语法格式?

[修饰符列表] @interface 注解类型名{

}

2.注解怎么使用,用在什么地方?

注解使用时的语法格式:

​ @注解类型名

注解可以出现在类上、属性上、方法上、变量上等。。。

注解还可以出现在注解类型上

默认情况下,注解可以出现在任意位置

3.JDK内置了哪些注解?

java.lang包下的注释类型:

​ 【掌握】Deprecated用@Deprecated注释的程序元素,不鼓励使用这样的元素,通常是因为它很危险或存在更好的选择

这个注解标注的元素已过时,向其他程序员传达一个信息,告知已过时,有更好的解决方案存在

​ 【掌握】Override表示一个方法声明打算重写超类中的另一个方法声明

@Override:

​ 只能注解方法

​ 这个注解是给编译器参考的,和运行阶段没有关系

​ 凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错

​ 【无须掌握】SuppressWarnings指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告

4.元注解

用来标注“注解类型”的“注解”,是元注解

常见的元注解有哪些?

Target:用来标注标注“被标注的注解”可以出现在哪些位置上–构造方法上、字段上、局部变量上、方法上…类上…

@target(ElenmentType.METHOD):表示“被标注的注解”只能出现在方法上

Retention:用来标注“被标注的注解”可以出现在哪些位置上

public @interface Retention{
	//属性
	RetentionPolicy value();
}

@Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中

@Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中

@Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中

5.注解定义属性

如果一个注解中有属性,那么必须给属性赋值(除非该属性使用default制定了默认值)

@MyAnnotation(name = "zhangsan")
    public void doSome(){

    }

如果一个注解的属性的名字是value,且只有一个属性时,使用时,该属性名可以省略

@MyAnnotation("haha")

6.注解中的属性类型

注解中的属性可以是哪一种类型?

byte、short、int、long、float、double、boolean、char

String、Class、枚举类型、以及以上每一种的数组形式**

如果数组中只有一个元素,大括号可以省略

@MyAnnotation(age = 25,email = {"zhangsan@123.com","zhangsan@sohu.com"})

7.反射注解

//获取这个类
        Class c = Class.forName("MyAnnotation.MyAnnotationTest");
        //判断类上是否有@MyAnnotation
        //System.out.println(c.isAnnotationPresent(MyAnnotation.class));//false

        if (c.isAnnotationPresent(MyAnnotation.class)){
            //获取该注解对象
            MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class);
            System.out.println("类上面的注解对象" + myAnnotation);
            //获取注解对象的属性(和调用接口一样)


        }
        //判断String类上是否存在这个注解
        Class stringClass = Class.forName("java.lang.String");
        System.out.println(stringClass.isAnnotationPresent(MyAnnotation.class));//false

8.通过反射获取注解对象属性的值

在这里插入图片描述

9.注解在开发中有什么用?

需求

​ 当一个类上面有@Id注解时,要求类中必须粗在int类型的id属性

如果没有int类型的id属性则报异常

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值